home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Assembly Programming Journal / apj_2.txt < prev    next >
Encoding:
Text File  |  2000-05-25  |  179.3 KB  |  4,668 lines

  1. ::/ \::::::.
  2. :/___\:::::::.
  3. /|    \::::::::.
  4. :|   _/\:::::::::.
  5. :| _|\  \::::::::::.                                              Dec 98/Jan 99
  6. :::\_____\::::::::::.                                             Issue       2
  7. ::::::::::::::::::::::.........................................................
  8.  
  9.             A S S E M B L Y   P R O G R A M M I N G   J O U R N A L
  10.                       http://asmjournal.freeservers.com
  11.                            asmjournal@mailcity.com
  12.  
  13.  
  14.  
  15.  
  16. T A B L E   O F   C O N T E N T S
  17. ----------------------------------------------------------------------
  18. Introduction...................................................mammon_
  19.  
  20. "Keygen Coding Competition".................................Ghiribizzo
  21.  
  22. "How to Use A86 for Beginners".................................Linuxjr
  23.  
  24. "Using the Gnu AS Assembler"...................................mammon_
  25.  
  26. "A Guide to NASM for TASM Coders"..................................Gij
  27.  
  28. "Tips on saving bytes in ASM programs"...................Larry Hammick
  29.  
  30. Column: Win32 Assembly Programming
  31.     "A Simple Window".........................................Iczelion
  32.     "Painting with Text"......................................Iczelion
  33.  
  34. Column: The C Standard Library in Assembly
  35.     "The _Xprintf functions"....................................Xbios2
  36.  
  37. Column: The Unix World
  38.     "X-Windows in Assembly Language: Part I"...................mammon_
  39.  
  40. Column: Assembly Language Snippets
  41.     "IsASCII?"............................................Troy Benoist
  42.     "ENUM, CallTable"..........................................mammon_
  43.  
  44. Column: Issue Solution
  45.     "PE Solution"...............................................Xbios2
  46. ----------------------------------------------------------------------
  47.       +++++++++++++++++++++++Issue Challenge++++++++++++++++++++
  48.  Write the smallest possible PE program that outputs its command line
  49. ----------------------------------------------------------------------
  50.  
  51.  
  52.  
  53.  
  54. ::/ \::::::.
  55. :/___\:::::::.
  56. /|    \::::::::.
  57. :|   _/\:::::::::.
  58. :| _|\  \::::::::::.
  59. :::\_____\:::::::::::..............................................INTRODUCTION
  60.                                                                      by mammon_
  61.  
  62.  
  63. Wow! This issue is huge. More than twice the size of the last; maybe it is time
  64. to go monthly...
  65.  
  66. This issue has as its theme --such as it were-- the use of popular free- and 
  67. shareware assemblers. It began with my needing to write a GAS intro to 
  68. accompany my X-Windows article; shortly thereafter, Linuxjr emailed me the 
  69. benefits of his university training with his A86 tutorial (beginners: this is 
  70. for you! Linuxjr explains *everything*). I then appealed to Gij to allow me to 
  71. incorporate his Nasm 'Quick-Start' guiide, which I have used often...he posted 
  72. the condition that I edit it heavily ;)
  73.  
  74. I would like to draw your attention first to our new column: Assembly Language 
  75. Snippets. Originally this was an idea which I and a few others had; however, I 
  76. never received any contributions for the 'Snippets' section. Then I received an
  77. email from  Troy with the first one... I pulled the rest out of my various asm 
  78. sources and voila, a new column was born. This is something that is fully open 
  79. to contributions; asm snippets --and we will need lots-- may be emailed to 
  80. asmjournal@mailcity.com or mammon_@hotmail.com, or they may be posted to the 
  81. Message Board at http://pluto.beseen.com/boardroom/q/19784/
  82. Basic format should be:
  83. ;Name:                Name to title you with
  84. ;Routine Title:       Name to title the snippet with
  85. ;Summary:             One-Line Description
  86. ;Comaptibility        Specific Assemblers or OSes this works with
  87. ;Notes:               Any extra notes you have
  88. --Code--
  89.  
  90.  
  91. I should point out here that freeservers.com is not very reliable; thus the 
  92. APJ home page is inaccessible more often than not. For this reason I have set 
  93. up a mirror on my own page, at http://www.eccentrica.org/Mammon/APJ/index.html
  94.  
  95. As for this issue's articles, we once again have two fine Win32 asm tutorials 
  96. by Iczelion, who maintains an excellent page at http://iczelion.cjb.net (with 
  97. a Win32 asm message board!). Ghirribizzo has supplied his fun Key Generator 
  98. Competition results (I can't say I was surprised when I saw the winner ;).  
  99.  
  100. Larry Hammick --who also maintains an excellent, smoking-enabled page at 
  101. http://www3.bc.sympatico.ca/hammick/-- has contributed a fantastic piece on asm
  102. optimization. XBios2 has this time gone above and beyond, not only with the C 
  103. Language in Assembly but with his Issue Challenge as well... asm coders and
  104. reverse engineers alike should read this.
  105.  
  106. As for the issue challenge, XBios2 did not provide me with one for next issue, 
  107. so I used one from a text I found on the Internet somewhere... he has been 
  108. emailed the text and can try to beat it ;) Also, I am going to be setting up a 
  109. page for reader responses to the Issue Challenges -- readers can anticipate the 
  110. solutions before each issue comes out, or try and best the solution afterwards. 
  111. Submissions can be sent to the same places as the Snippets. 
  112.  
  113. Author Bio's? I know mainstream mags do this-- if you want one, send one. I'll 
  114. tack it onto the end of the article ... anything within reason: URL, email, 
  115. hobbies, perversions, favorite drink, favorite linux distro, etc.
  116.  
  117. Next Issue: How many articles on Code Optimization can I get? That would make a
  118. great theme (with the foundation laid this issue)--anything from code theory to 
  119. PentiumII-specific optimizations would be welcome. Prospective articles, send 
  120. to me or post on the MB...no topic is unacceptable unless you can in no way 
  121. possible relate it to assembly language.
  122.  
  123. Enjoy the ish, 
  124. _m
  125.  
  126.  
  127.  
  128.  
  129. ::/ \::::::.
  130. :/___\:::::::.
  131. /|    \::::::::.
  132. :|   _/\:::::::::.
  133. :| _|\  \::::::::::.
  134. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  135.                                                       Keygen Coding Competition
  136.                                                       by Ghiribizzo
  137.  
  138.  
  139. Introduction
  140. ------------
  141. The competition was to write the smallest key generator for the simple serial 
  142. scheme I wrote as a trainer for newbies. I had a few reasons for starting this 
  143. competition:
  144. ╖ To give the newbies a chance to participate in a competition
  145. ╖ To give old hands the chance to brush up on their assembly skills
  146. ╖ To promote tight assembly coding
  147. ╖ To demonstrate the various different methods used to improve efficiency in 
  148.   coding
  149.  
  150. Well, I'm back from my short European jaunt and the competition is now closed. 
  151. I have greatly enjoyed the entire competition, from the coding of the crackme 
  152. and the chats with various crackers on IRC through to deciding the winner and 
  153. writing this document.
  154.  
  155. Analysis of the Serial Scheme
  156. -----------------------------
  157. The serial scheme was kept deliberately simple as it was written for newbies to 
  158. train with. The scheme took a name of up to 16 bytes long and required a 16 
  159. byte serial number. There was a 256 byte lookup table that was indexed directly 
  160. with the ASCII values of the name field. The name was padded to a length of 16 
  161. (if necessary) using values hardcoded into the scheme. The 256 byte lookup 
  162. table was created using eight maximal 8 bit linear feedback shift registers 
  163. (LFSRs) in parallel i.e. producing one output byte per 'clock'. The LFSRs were 
  164. initialised to produce 'Ghir_OCU' as the first 8 output bytes. The table was 
  165. precomputed and it was not expected that the cracker recognise the nature of 
  166. the lookup table - although a post I made to the cracking forum about LFSRs
  167. might have tipped the more astute crackers!
  168.  
  169. The rules of the competition required that some standard interface text be 
  170. included which strongly urged the use of service 9 interrupt 21h - though this 
  171. would probably be used in any case - and discouraged blank screens and other 
  172. unfriendly UIs from being used to save bytes. Also, the rules specified a range 
  173. of input to be handled smaller than the possible 256 maximum. Due to the simple 
  174. nature of the serial scheme, this meant that the lookup table could immediately 
  175. be stripped down to the input range.
  176.  
  177. I envisioned that there would be 3 æfightsÆ. One to reduce the original 
  178. algorithm, second to reduce the packed table lookup algorithm and the last to 
  179. reduce the LFSR algorithm. As it turned it, everyone seemed to go for the
  180. packed table option.
  181.  
  182. The Entrants
  183. ------------
  184. The following entrants have been included because they illustrate the different 
  185. ideas and methods used to reach the common goal of reducing code size. I didn't 
  186. realise that so many crackers would use the precomputed table method. Perhaps 
  187. word got out during IRC chats and everybody started using them? In any case, 
  188. this didnÆt reduce the size cutting war as precomputation had its own routines 
  189. that needed to be optimised.
  190.  
  191. Ghiribizzo Alpha (223 bytes)
  192. ----------------------------
  193. This was not an entrant as it would hardly be fair for me to enter the 
  194. competition knowing how the lookup table was generated! This keygen was 
  195. basically converted from the crackme and improved 'on-the-fly' by generating 
  196. the lookup table in code and tidying up routines where they were obviously 
  197. inefficient. No great thought went into this and the code size was just to give 
  198. myself an idea of what crackers would be aiming for. Aside from generating the 
  199. lookup table, the only other unusual feature of this keygen was the use of the 
  200. XLAT command instead of the standard indexing used in the crackme. I didn't 
  201. stop to check whether this used less space or not, but included it as newbies 
  202. may not be familiar with the XLAT instruction. As it happened, the  XLAT 
  203. instruction was used in SpyderÆs keygen.
  204.  
  205. From the size I got from this keygen, I tried to guess a required key input 
  206. range to put this size between thestraight table precomputation and the packed 
  207. table precomputation.
  208.  
  209. One thing to note is how I ended the program. I was quite surprised by the fact 
  210. that nobody else seemed to know that you could quit com programs with a ret 
  211. instruction. Further size savings can be made by using BbÆs trick of keeping DH 
  212. and also by tweaking the generator to fix some of the bitstreams produced to 
  213. give us the bits we need and save later processing.
  214.  
  215. Cruehead Alpha (244 bytes)
  216. --------------------------
  217. I got this from Cruehead on IRC when I asked to see what he had managed so far. 
  218. Although this version is unfinished it is still impressive. The keygen relies 
  219. on precomputing the whole table and reducing the keygen to a single table 
  220. lookup.
  221.  
  222. The coding is very simple - almost seems as if Cruehead was typing the steps 
  223. going through his head straight onto the keyboard (perhaps he was?) the 
  224. resulting code is consequently very easy to understand and follow.
  225.  
  226. Bb #10 (230 bytes)
  227. ------------------
  228. Bb has written an excellent keygen. He has put some serious hard work into this 
  229. including taking the time to calculate the dx offsets manually instead of just 
  230. using the æoffsetÆ feature that the compiler provides. It has been fun watching 
  231. BbÆs keygen progress as the first one I received was version 5 which was 256 
  232. bytes long. The keygen presented here is version 10. There are other nice bits 
  233. and bobs throughout this code. This makes it quite frustrating as in various 
  234. places so much space is blatantly wasted. Just take a look at the last 6 lines 
  235. of code! There shouldnÆt even be 6 lines there! IÆm sure Bb will learn a lot 
  236. from seeing some of the other keygens here and IÆm sure he will do very well 
  237. should he enter the next competition.
  238.  
  239. Spyder (211 bytes)
  240. ------------------
  241. Tidy, compact and elegantly coded. A little sparse in commenting (it seems like 
  242. Spyder coerced IDA to write the keygen for him ;-p). The table lookup is an 
  243. interesting piece of code.
  244.  
  245. VoidLord (247 bytes)
  246. --------------------
  247. Another keygen using the idea of a packed precomputed table. VoidLordÆs first 
  248. keygen. LetÆs hope we see more!
  249.  
  250. Honourable Mentions
  251. -------------------
  252. Special mention given to Trykka who managed to deduce how the look-up table was 
  253. created - but never sent in an entry!
  254.  
  255. The Winner
  256. ----------
  257. Well it looks like Spyder is the winner by quite a large margin. Incidentally, 
  258. I have just made a quick check that the keygens work. You might be able to bump 
  259. yourself up on the scale by picking holes in the other keygens :-)
  260.  
  261. Rankings
  262. --------
  263.  
  264.   __Keygen______Size________Author______
  265.     kgen.com    211         Spyder
  266.     kg.com      224         Ghiribizzo (alpha)
  267.     kg10.com    230         Bb
  268.     kg9.com     233         Bb
  269.     kg6.com     239         Bb
  270.     kgvoid.com  247         VoidLord
  271.     kgcrue.com  255         Cruehead (alpha)
  272.     kg5.com     256         Bb
  273.     kgt.com     529         Serial Scheme
  274.  
  275. Final Words
  276. -----------
  277. There have been some excellent ideas in the keygens. However, none of the 
  278. keygens are as small as they could be. They all have some scope for improvement. 
  279. By combining some of the ideas given in the above keygens, we could create a 
  280. new smaller keygen. It will be interesting to see what the smallest possible 
  281. keygen would look like.
  282.  
  283. I hope that everyone who has taken part in the competition, or who has followed 
  284. it, has gained something from it. I hope that there will be more entries for 
  285. the next competition!
  286.  
  287. The Source Codes
  288. ----------------
  289.  
  290. ; GhiribizzoÆs Keygen =========================================================
  291. .model tiny
  292. .386
  293. .code
  294. .startup
  295. ; The first part of the code is the table generator
  296. ; Note that we can actually do some æprecomputingÆ by
  297. ; fixing some of the bits in the generator to produce
  298. ; the bits that we need. This will save some bytes
  299. ; in the serial section. I have not bothered to do this.
  300.     mov ax, 5547h
  301.     mov bx, 6869h
  302.     mov cx, 725fh
  303.     mov dx, 4f43h
  304.     mov di, offset PRD
  305.     mov si, offset PRD + 0ffh
  306. LFSR:
  307.     stosb
  308.     ;Save MSB
  309.     mov bp,ax
  310.     mov al,ah
  311.     and ax,0ffh
  312.     xchg ax,bp
  313.     ;Tap
  314.     xor ah,bl
  315.     xor ah,ch
  316.     xor ah,al
  317.     ;Shift
  318.     mov al,bh
  319.     mov bh,bl
  320.     mov bl,ch
  321.     mov ch,cl
  322.     mov cl,dh
  323.     mov dh,dl
  324.     ;Store MSB
  325.     and dx,0ff00h
  326.     or dx,bp
  327.     cmp di,si
  328.     jle LFSR
  329. ;-----------------------------------------------------------------
  330.     mov ah,9
  331.     mov dx,offset startMsg
  332.     int 21h
  333.     mov ah,10
  334.     mov dx,offset NameInput
  335.     int 21h
  336. ;-----------------------------------------------------------------
  337.     mov si,offset NameBuffer
  338.     mov di,offset NameHash
  339.     mov bx,offset Table1
  340. MakeSerial:
  341.     lodsb
  342.     xlat
  343.     and al,3fh
  344.     or al,30h
  345. byteOK:
  346.     cmp al,39h
  347.     jle keepit
  348.     add al,7
  349. keepit:
  350.     stosb
  351.     cmp di,offset stopbyte
  352.     jl MakeSerial
  353. ;-----------------------------------------------------------------
  354.     mov dx,offset NH2
  355. printMsg:
  356.     mov ah,9
  357.     int 21h
  358. exit:
  359.     ret
  360. StartMsg    db      0dh,0ah,'OCU Keggen #1 ',0feh,' Ghiribizzo 1998 ',0dh,0ah
  361.             db      0dh,0ah,'Enter Name : $'
  362. NameInput   db      17
  363. NameRead    db      ?
  364. NameBuffer  db      'mk3 "![]ns)%3x#0Z'
  365. nh2         db      0dh,0ah,'Serial Number: '
  366. NameHash    db      16 dup('y')
  367. stopbyte    db      0dh,0ah,'$'
  368. Table1:
  369. PRD:
  370. END
  371.  
  372. ; CrueheadÆs Keygen ===========================================================
  373. .model tiny
  374. .386
  375. .stack
  376. .data
  377. StartMsg    db      0dh,0ah,'OCU Keggen #1 ',0feh,' Cruehead 1998 ',0dh,0ah
  378.             db      0dh,0ah,'Enter Name : $'
  379. SerialMsg   db      0dh,0ah,'Serial Number: '
  380. NameVar     db      011h,0h,06Bh,06bh,033h,020h,022h,021h,05bh,05dh,06eh
  381.             db      073h,029h,025h,033h,078h,023h,030h,'$'
  382. Table       db      037h,035h,034h,031h,036h,032h,046h,044h,046h,044h,044h
  383.             db      031h,035h,035h,038h,035h,036h,046h,032h,045h,036h,030h
  384.             db      031h,039h,033h,034h,030h,046h,031h,042h,044h,030h,043h
  385.             db      036h,043h,035h,039h,045h,039h,033h,036h,043h,037h,035h
  386.             db      036h,044h,045h,036h,032h,044h,031h,037h,039h,030h,031h
  387.             db      042h,046h,043h,034h,032h,031h,035h,037h,034h,044h,032h
  388.             db      032h,032h,030h,043h,034h,030h,044h,044h,033h,039h,044h
  389.             db      043h,038h,036h,031h,038h,041h,037h,034h,046h,045h,041h
  390.             db      036h,044h,043h,041h,041h,039h,043h,037h
  391. .code
  392. .startup
  393.     mov ah,09h
  394.     lea dx,StartMsg
  395.     int 21h
  396.     mov ah,0ah
  397.     lea dx,NameVar
  398.     int 21h
  399. OnceAgain:
  400.     mov bl,NameVar[di+4]
  401.     cmp bl,0dh
  402.     jne noprob
  403.     mov bl,02bh
  404. noprob:
  405.     mov al,table[bx-020h]
  406.     mov NameVar[di+2],al
  407.     inc di
  408.     cmp di,0Eh
  409.     jne OnceAgain
  410.     mov word ptr NameVar[16],00a0dh
  411.     mov ah,09h
  412.     lea dx,SerialMsg
  413.     int 21h
  414. .exit
  415. end
  416.  
  417. ; BbÆs Keygen =================================================================
  418. ; KG10 - Ghiribizzo KeyGen
  419. ; written by bb 12Sep98 1:30AM
  420. ; next revision 13Sep98 5:00PM
  421. ; yet more changes - 26Sep98 - late late night
  422. ; eat 3 more bytes 28Sep98
  423. ;
  424. ; comments where the evils lay
  425. ;
  426. ; I just knew that I HAD to make this thing 256 bytes of less. Beware: This
  427. ; is NOT an example of good coding practice! I almost wish I could do a
  428. ; "bytes saved" comparison for all the little hacks.
  429. ;
  430. ; I've gotten this to assemble under TASM. It MUST assemble as a 16-bit COM file,
  431. ; and even then I can't guarantee that the offsets will remain stable between
  432. ; various assemblers. Let me restate that: I CAN guarantee that this won't
  433. ; work for you when you try and assemble it yourself. :)
  434. ;
  435. P8086
  436. MODEL TINY
  437. DATASEG
  438. OffsetStartMsg      EQU     52h
  439. OffsetMySerial      EQU     7fh
  440. OffsetSerialMsg     EQU     91h
  441. OffsetMyName        EQU     0a3h
  442. StartMsg    db  0dh,0ah,'OCU Keggen #1 ',0feh,' ----- bb ----- 1998',0dh,0ah
  443. ; There's no reason not to re-use this section of the StartMsg, since it fits 
  444. ; perfectly though code had to be added to affix a linefeed
  445. MySerial    db  0dh,0ah,'Enter Name : $'
  446. SerialMsg   db  0dh,0ah,'Serial Number: $'
  447. ; previous change to MyName not needed anymore
  448. MyName      db  11h, 0h, 6Dh, 6Bh, 33h, 20h, 22h, 21h, 5Bh, 5Dh, 6Eh, 73h, 29h, 
  449.             db  25h, 33h, 78h, 23h, 30h, 5Ah
  450. ; Not only does the full table not need to be used, but since it's basically a 
  451. ; substitution cypher we can fit everything into these 96 or so bytes
  452. ; Also, the trailing commented-out 37h saves us one byte. It's the substitution for 7Fh,
  453. ; but since 7Fh is a DELETE when using 0a/int21h, it never gets accepted by KGT.COM or by
  454. ; this keygen. Therefore, it's useless and unneeded.
  455. ; NewTable db '754162FDFDD155856F2E6019340F1BD0C6C59E936C756DE62D17901BFC421574
  456. ; D2220C40DD39DC8618A74FEA6DCAA9C';, 37h
  457. ; and I missed the fact that it also only uses characters 0-9 and A-F
  458. ; which can be expressed in 4 bits, cutting the 96 byte table in half
  459.  
  460. NewTable    db      75h, 41h, 62h, 0FDh, 0FDh, 0D1h, 55h, 85h
  461.             db      6Fh, 2Eh, 60h, 19h, 34h, 0Fh, 1Bh, 0D0h
  462.             db      0C6h, 0C5h, 9Eh, 93h, 6Ch, 75h, 6Dh, 0E6h
  463.             db      2Dh, 17h, 90h, 1Bh, 0FCh, 42h, 15h, 74h
  464.             db      0D2h, 22h, 0Ch, 40h, 0DDh, 39h, 0DCh, 86h
  465.             db      18h, 0A7h, 4Fh, 0EAh, 6Dh, 0CAh, 0A9h, 0C7h
  466. CODESEG
  467. STARTUPCODE
  468. ; A note here: We're at <256 bytes and we fit snugly between 0100h-0200h in memory.
  469. ; Therefore, any offset to text that we need is going to have a constant value for
  470. ; DH, namely 01h. By initializing DH once at this next line of code, we never need
  471. ; to change DH again, only DL. We'll save a few bytes here and there because of it,
  472. ; though it's more work to find the offsets manually after assembly, and then hard-
  473. ;coding them in and re-assembling. I suppose there might be some construct like
  474. ; offset ( MyName AND 00ffh ), but I didn't really look into it. EQU will work.
  475.     mov dx, offset StartMsg
  476.     mov ah,09h
  477.     int 21h
  478.     ; save a byte
  479.     mov dl, OffsetMyName
  480.     mov ah,0ah
  481. ; Now that we're through with the StartMsg, we can adjust MySerial to print a linefeed.
  482. ; I can save a byte here by using the AH register instead of a 0AH immediate value,
  483. ; since AH is now set to 0AH for the int21 get-string-from-keyboard.
  484.     mov [MySerial+10h], ah
  485.     int 21h
  486.     ; 2 into DL for a division during the main loop
  487.     mov dl, 2
  488. ; We start at the END of MyName and work our way backwards, because we can avoid the CMP
  489. ; and simply check for the Signed flag when BP rolls over. We save a couple of bytes.
  490.     mov bp, 0fh
  491. ; Also, I shaved a few bytes out of this by using BP in place of BX, avoiding the PUSH/POPs
  492. ; which I shouldn't have done anyway since I didn't define a new stack for the application.
  493. loop1: 
  494.     xor ah, ah ; need to clear ah and bh, unfortunately.
  495.     xor bh, bh
  496.     mov al, [bp+MyName+2]
  497.     sub al,20h ; if the sub sets carry, then we're probably the carriage return
  498.     jnc skipcr ; so we'll set ourselves = to something that has the same table
  499.     mov al, 03h ; lookup value as the carriage return.
  500. skipcr:
  501.     div dl ;after the DIV, AL will be two table values, and AH will decide which
  502.            ; one we should use
  503.     mov bl, al ; we need table lookup through bx, not al
  504.     mov al,[bx+NewTable]
  505.     test ah, dh ;since dh always=1,test ah,dh will save us a byte over test ah,01
  506.     jne skipshift ; if AH=0, use least significant nibble
  507.     ; if AH=1, use most significant nibble by shifting MSN into LSN
  508.     ; TASM assembles shr al, 4 as shr al, 1 four times.. we don't want that.
  509.     db 0c0h, 0e8h, 4 ; shr al, 4
  510. skipshift:
  511.     and al, 0Fh ; strip off high nibble
  512.     add al, 30h ; and turn into printable [0-9A-F] character
  513.     cmp al, 39h
  514.     jle numnum
  515.     add al, 7
  516. numnum:
  517.     mov [MySerial+bp],al
  518.     dec bp
  519.     jns loop1 ; loop until bp flips
  520.     ; save another "offset" byte
  521.     mov dl, OffsetSerialMsg
  522.     mov ah, 9
  523.     int 21h
  524.     ; save another byte
  525.     mov dl, OffsetMySerial
  526.     ; AH should already == 9, no need to specify it here.
  527.     int 21h
  528.     ; End of the line
  529.     mov ah,4ch
  530.     int 21h
  531. END
  532.  
  533. ; SpyderÆs Keygen==============================================================
  534. ; Ghiribizzo's Key Generator Competition entry by Spyder
  535. ; Sheesh you get assembler source and you want comments?
  536. ; Only one nibble of each byte in the original key table holds useful
  537. ; information. Only key table entries in the range 20..0x7F and 0x0D are
  538. ; needed - those 60 nibbles are packed into a 30 byte table, 0x0D is handled
  539. ; as a special case.
  540. ; The rest is just space concious assembler with a few wrinkles to save
  541. ; bytes. I worry I may have missed some pattern in the key table, could it
  542. ; be generated or derived? Otherwise I'm pretty happy with the result.
  543. .286
  544. seg000 segment byte public 'CODE'
  545. assume cs:seg000
  546. org 100h
  547. assume es:nothing, ss:nothing, ds:seg000
  548. public start
  549. start proc near
  550.     mov ah, 9
  551.     mov dx, offset StartMsg
  552.     int 21h ; Sign on
  553.     mov ah, 0Ah
  554.     mov dx, offset Buffer
  555.     int 21h ; Get name
  556.     mov si, offset BufferCont ; Set up for loop
  557.     mov di, offset Serial
  558.     mov bx, offset Key - 10h
  559.     xor ax,ax
  560.     mov cx,10h
  561. loop1: 
  562.     lodsb
  563.     ; cmp al,0dh ; don't need this because we arranged the data
  564.     ; jnz skip0 ; before the key table to give the right code
  565.     ; mov al,'p' ; for this out of range case
  566. skip0: 
  567.     sar al,1
  568.     xlat
  569.     jc skip1
  570.     sar al,4
  571. skip1: 
  572.     and al,0fh
  573.     add al,'0'
  574.     cmp al,'9'
  575.     jle skip2
  576.     add al,7
  577. skip2: 
  578.     stosb
  579.     loop loop1
  580.     movsw
  581.     movsb
  582.     mov ah,9
  583.     mov dx,offset SerialMsg
  584.     int 21h
  585.     int 20h
  586. start endp
  587. Buffer      db  11h ;
  588.             db  0 ;
  589. BufferCont  db 'm'
  590.             db 'k'
  591.             db '3'
  592.             db ' '
  593.             db '"'
  594.             db '!'
  595.             db '['
  596.             db ']'
  597.             db 'n'
  598.             db 's'
  599.             db ')'
  600.             db '%'
  601.             db '3'
  602.             db 'x'
  603.             db '#'
  604.             db '0'
  605.             db 0dh, 0ah, '$'
  606. StartMsg    db 0dh,0ah,'OCU Keggen #1 ',0feh,' ----- spyder ----- 1998',0dh,0ah
  607.             db 0dh,0ah,'Enter Name : $'
  608.             db 0 ; A crucial spacer
  609. Key         db 075h, 041h, 062h, 0FDh, 0FDh, 0D1h, 055h, 085h
  610.             db 06Fh, 02Eh, 060h, 019h, 034h, 00Fh, 01Bh, 0D0h
  611.             db 0C6h, 0C5h, 09Eh, 093h, 06Ch, 075h, 06Dh, 0E6h
  612.             db 02Dh, 017h, 090h, 01Bh, 0FCh, 042h, 015h, 074h
  613.             db 0D2h, 022h, 00Ch, 040h, 0DDh, 039h, 0DCh, 086h
  614.             db 018h, 0A7h, 04Fh, 0EAh, 06Dh, 0CAh, 0A9h, 0C7h
  615. SerialMsg   db 0dh,0ah,'Serial Number: '
  616. Serial:
  617. seg000 ends
  618. end start
  619.  
  620. ; VoidLordÆs Keygen============================================================
  621. ; OCU Keygen #1 | VoidLord 1998
  622. ; Category: newbie (this is my first keygen)
  623. ; Solution:
  624. ; for the every possible input char (20h-7fh) the "serial" char is stored in the 
  625. ; Table. Since the output chars can only be 0-9 and A-F, we can store two chars 
  626. ; in one byte, reducing the table size to 48 bytes.
  627. seg000 segment byte public 'CODE'
  628. assume cs:seg000
  629. org 100h
  630. assume es:nothing, ss:nothing, ds:seg000
  631. start proc near
  632.     mov ah, 9 ; DOS - Write starting message
  633.     lea dx, StartMsg
  634.     int 21h
  635.     mov ah, 0ah ; DOS - read Name
  636.     lea dx, Serial
  637.     int 21h
  638.     xor ax, ax
  639.     xor bx, bx
  640. loop1: 
  641.     mov al, [Serial2+bx] ; the output will be in the same buffer
  642.     cmp al, 0dh ; end of input string (odh) ?
  643.     jne no_cr
  644.     mov [Serial2+bx], '1' ; the output char will be '1'
  645.     jmp finish ; the remaining chars are OK already
  646. no_cr: 
  647.     push bx ; now we should translate the namechar
  648.     sub al, 20h ; to the serial number char, using the
  649.     mov bx,ax ; lookup Table
  650.     shr bx,1 ; we have two chars in one byte in the Table
  651.     and al, 1
  652.     jnz odd ; is this char "even or odd" ?
  653.     mov al, [Table1+bx]
  654.     and al, 0fh ; if even, use the lower 4 bits
  655.     jmp end_l
  656. odd: 
  657.     mov al, [Table1+bx]
  658.     mov cl, 4
  659.     shr al, cl ; if odd, use the higher 4 bits
  660. end_l: 
  661.     pop bx ; translate the number to the hex char
  662.     cmp al, 10 ; is it digit 0-9 or letter A-F
  663.     jl digit
  664.     add al, 7 ; if letter, add 7
  665. digit: 
  666.     add al, '0' ; if digit, just add '0'
  667.     mov [Serial2+bx], al
  668.     inc bx ; process next input char
  669.     cmp bx, 10h
  670.     jl loop1
  671. finish: 
  672.     mov Serial, ':' ; complete the output string
  673.     mov Serial+1,' '
  674.     mov ah, 9 ; DOS - Print solution
  675.     lea dx, SerialMsg
  676.     int 21h
  677.     mov ah, 4Ch ; DOS - QUIT with EXIT
  678.     int 21h
  679. start endp
  680. StartMsg        db      0dh,0ah,'OCU Keygen #1 ',0feh
  681.                 db      ' ----- VoidLord ----- 1998'
  682.                 db      0dh, 0ah, 0dh,0ah,'Enter Name : $'
  683. SerialMsg       db      0dh,0ah,'Serial Number'
  684. Serial          db      11h, 0
  685. Serial2         db      67, 57, 69, 55, 52, 53, 50, 53, 56, 55
  686.                 db      68, 50, 69, 54, 49, 54, 0dh, 0ah, '$'
  687. Table1          db      87, 20, 38, 223, 223, 29, 85, 88, 246, 226
  688.                 db      6, 145, 67, 240, 177, 13, 108, 92, 233, 57
  689.                 db      198, 87, 214, 110, 210, 113, 9, 177, 207, 36
  690.                 db      81, 71, 45, 34, 192, 4, 221, 147, 205, 104
  691.                 db      129, 122, 244, 174, 214, 172, 154, 124
  692. seg000 ends
  693. end start
  694.  
  695.  
  696. ::/ \::::::.
  697. :/___\:::::::.
  698. /|    \::::::::.
  699. :|   _/\:::::::::.
  700. :| _|\  \::::::::::.
  701. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  702.                                                    How to Use A86 for Beginners
  703.                                                    by Linuxjr
  704.  
  705.  
  706. Requirements:
  707. -Basic Dos knowledge like copying and renaming files and such
  708.  
  709. I am writing this paper for I find plenty of tutorials and books all about
  710. assembly and how to write programs and how to do loops, if/else statments,
  711. etc... But one thing I did not see plenty of is tutorials on how to set up the
  712. assembler of choice that you grow fond of, for instance nasm, a86, tasm, masm
  713. GAS, etc. 
  714.  
  715. So I am writing about a86 and I'm using my college notes and experience I 
  716. learned from my Assembly class. I hope this will help you enjoy a86 and
  717. encourage you to learn how to manage up to x286 opcodes and 16-bit code
  718. before you start tackling with 32bit and Windows programming in assembler. 
  719. This is a sort of warning that you will only be able to write DOS programs
  720. but you have to learn how to crawl before you can walk, and you have to learn
  721. how to walk before you can run. I hope to show you how to set up a86, how
  722. to write a few simple programs with the template I use, and how to do some
  723. basic stuff in assembler.
  724.  
  725. I took a college course on Assembly a couple of months ago, and I was happy to
  726. learn the internals of the system and how to manipulate the registers for some
  727. awesome results. The assembler that we used was a86 by Eric Isaacson. This
  728. is a shareware program, meaning you get to play with it before buying it. To 
  729. get this assembler go to - http://www.eji.com/a86/ - and you will see where to
  730. download the programs. It is in a zip file and you just unzip it with your
  731. favorite program like winzip or pkunzip. You should also download d86, the
  732. debugger, for use with your a86 programs. Once you downloaded them, unzip the
  733. files to a directory such as c:\a86, or even put on a floppy disk if you are
  734. worried about space.
  735.  
  736.  
  737. Getting Started
  738. ---------------
  739. Let's get into it: you've got the assembler and the debugger, what next? First
  740. of all, we have to make a text file since all asm source code is nothing but a 
  741. plain text code that has a bunch of operands and functions to do what you want 
  742. your program to do. 
  743.  
  744. I start all my a86 programming by opening up my template.asm, which what I 
  745. got from school; it is a useful template and it makes a good dos .EXE when you
  746. compile it with the supplied batch file. Cut the following code and save it in 
  747. a text file called template.asm:
  748.  
  749. X--------Begin Cutting here--------------------------------------------
  750.  
  751. ; PROGRAM             : 
  752. ;
  753. ; AUTHOR              : 
  754. ;
  755. ; PURPOSE             : 
  756. ;                       
  757. ; PROGRAM OUTLINE     : 
  758. ;                       
  759. ;============================== EXTERN ===============================
  760.  
  761. ;=========================== STACK SEGMENT ===========================
  762. sseg   segment      para stack 'STACK'
  763.                   
  764.     db      100H dup ( ? )        ; allow 256 bytes of memory for
  765.                                   ; use by our program stack.
  766. sseg   ends
  767.  
  768. ;============================ DATA SEGMENT ============================
  769. dseg    segment     para 'DATA'
  770.  
  771. dseg    ends
  772.  
  773. ;============================ CODE SEGMENT ============================
  774. cseg    segment     para 'CODE'
  775. ; Begin the Code segment containing executable machine code
  776.  
  777. program proc    far             ; Actual program code is completely
  778.                                 ; contained in the FAR procedure 
  779.                                 ; named PROGRAM 
  780.  
  781.     assume  cs:cseg, ds:dseg, ss:sseg
  782. ; Set Data Segment Register to point to the Data Segment of this program
  783.     mov     ax,dseg
  784.     mov     ds,ax
  785.  
  786. ;=============== Rest of MAIN PROGRAM code goes here ==================
  787. exit:
  788.     mov     ax,4c00h              ; terminate program execution and
  789.     int     21h                   ; transfer control to DOS
  790.  
  791. program endp                          ; end of the procedure program
  792.  
  793. ;============================ PROCEDURES ==============================
  794. cseg    ends                    ; End of the code segment containing
  795.                                 ; executable program.
  796.  
  797.     end     program         ; The final End statement signals the
  798.                             ; end of this program source file, and
  799.                             ; gives the starting address of the
  800.                             ; executable program
  801.  
  802. X--------Stop Cutting here--------------------------------------------
  803.  
  804. Now we have a template to use, and this is just one out of many templates you
  805. can make for your assembly programs. Now let's begin to have fun. These few
  806. programs will get us going for a basic feel of how to set up a basic hello
  807. program.
  808.  
  809. What we will learn from this example is:
  810. 1) The basic mechanics of editing the template file to get an ASM source code
  811.    file, assembling and linking it, and possibly fixing syntax errors.
  812. 2) Nearly all of the programs have loops in them, having different formats.
  813. 3) The operation of several INT 21H functions: 01H, 02H and 08H
  814.   (character input and output), 09H(string output), and 4CH(program termination)
  815. 4) The operation of the DOSIOLT procedures: inhex16 and outhex16, and how to
  816.    assemble and link a program that uses them.
  817. 5) Both string and numeric variables will be demonstrated.
  818.  
  819.  
  820. Creating an ASM file for the Message Program
  821. --------------------------------------------
  822. To become familiar with the process of creating an assembly program, you will
  823. create a simple program that prints a one line message. As with most
  824. programming languages, Assembly programming starts with a plain text file
  825. containing the program instructions to execute. Ordinarily, a programmer would
  826. have to type in the entire source file from scratch. But 8086 assembler
  827. program files contain a large number of setup directives and declarations
  828. which are essentially the same for every program. It will be easier to start
  829. with a file that has all the necessary directives and declarations already in
  830. it, and just add to it the actual program parts.
  831.  
  832. The file template.asm is that a template which contains all the necessary
  833. pieces of a program, except for the actual program itself. Make a copy of the
  834. template.asm file, and name it something appropriate: message.asm is a good
  835. choice. The file extension must be ASM. You will edit the new file to create
  836. your first program. DO NOT EDIT template.asm itself!!!! You will use this
  837. template file as the start of your assembly programs so it should not be 
  838. alterated(until you get advanced enough to play around with it ;-).
  839.  
  840. We will be using EDIT in a dos box as our editor, though you can use notepad or
  841. Ultraedit to edit your assembly files as well.
  842.  
  843. The Comments 
  844. All of the progras that you will write should have a descriptive set of header 
  845. comments at the top. Any text AFTER a semicolon is considered a comment. The 
  846. top of your new program file should already have the basic outline for this 
  847. comment. Edit in your message.asm file to have something like this :
  848.  
  849. ; PROGRAM             :  Message Program
  850. ;
  851. ; AUTHOR              :  Your Name here
  852. ;
  853. ; PURPOSE             : This program simply prints a one line message
  854. ;                       to the screen
  855. ; PROGRAM OUTLINE     : Use INT 21H Function 09H to print the message.
  856.  
  857. This is just an example to help you know what you want to do, and to have a
  858. reference if you were to walk away from a project for a year or so...the header
  859. will make a nice reminder of what you were trying to get this program to do.
  860.  
  861. The Ram Variable
  862. The program that you will creat in this part requires a variable. You will 
  863. create a string of characters labeled message. The part of the file where all 
  864. data is placed is the Data Segment. Look in your ASM file for the following 
  865. lines:
  866.  
  867. dseg    segment     para 'DATA'
  868.  
  869. dseg    ends
  870.  
  871. Change this part of the code so that the message to be printed is defined.
  872. The result will look like:
  873.  
  874. dseg    segment     para 'DATA'
  875.  
  876. message   db  0DH, 0AH, "WHOPPEEEE!!! My first Message.", 0DH, 0AH, "$"
  877.  
  878. dseg    ends
  879.  
  880. The HEX values are the two-byte sequence for a DOS newline(CR-LF). The first
  881. characters of "0DH" and "0AH" is ZERO, no capital O. Note that there is NO
  882. semicolon before "message". Do not allow this part to break over two lines.
  883.  
  884. THE Code - 
  885. Now locate the part of the code where the program code goes. It should look 
  886. like this:
  887.  
  888.  ;========================Main Program================================
  889.  ;
  890.  program proc    far             ; Actual program code is completely
  891.                                  ; contained in the FAR procedure 
  892.                                  ; named PROGRAM 
  893.  
  894.     assume  cs:cseg, ds:dseg, ss:sseg
  895. ; Set Data Segment Register to point to the Data Segment of this program
  896.     mov     ax,dseg
  897.     mov     ds,ax
  898.  
  899. ;=============== Rest of MAIN PROGRAM code goes here ==================
  900.  
  901. exit:
  902.  mov     ax,4c00h              ; terminate program execution and
  903.  int     21h                   ; transfer control to DOS
  904.  
  905. program endp                          ; end of the procedure program
  906.  
  907. ;============================ PROCEDURES ==============================
  908.  
  909.  
  910. cseg    ends                    ; End of the code segment containing
  911.     ; executable program.
  912.  
  913.  end     program         ; The final End statement signals the
  914.     ; end of this program source file, and
  915.     ; gives the starting address of the
  916.     ; executable program
  917.  
  918. All of the code for your program Should REPLACE the comment: 
  919. "Rest of Main Program code goes here".
  920.  
  921. Here is the code you will use to print out the message:
  922.  
  923.  ;Print the message
  924.  mov dx, offset message
  925.  mov     ah, 09H
  926.  int 21H
  927.  
  928. This code just calls the DOS Interrupt used to print strings to the screen.
  929. Interrupt 21H is a general starting point for many useful DOS calls. The
  930. sub-function used to print strings is Function 09H; this value must be loaded
  931. into the AH register before calling. Also, Int 21H Function 09H requires the
  932. address of the message be placed in the DX register. The above code performs
  933. these two initialization tasks, and then calls the interrupt.
  934.  
  935. Take careful note of the semicolons which start the comments. Also, do not
  936. alter any of the other part of the code.
  937.  
  938. These were the only two changes you needed to make.
  939.  
  940. Assembling with asm86.bat
  941. -------------------------
  942. Now we have written our first asm file. To assemble with a86 you could try to
  943. use the switches from the manual that is included with the a86 package, or
  944. you can make things easy by using this batch file, which is designed for 
  945. programs that use the template file. Here is the batch file:
  946.  
  947. :------------------------------ASM86-----------------------------------
  948. @echo off
  949. REM This is a simple batch file to use a86 and link:       
  950. if exist %1.asm GOTO FOUND
  951.                 echo %0 ERROR : %1.asm -- FILE NOT FOUND
  952.                 echo Usage: %0 file [link-file]
  953.                 GOTO STOP
  954.         : FOUND
  955.         :-- Assemble the program
  956.         echo a86 +O +S +E %1.asm
  957.         a86 +O +S +E %1.asm
  958. ::-- IF THERE WAS AN ERROR, STOP
  959.         IF ERRORLEVEL 1 GOTO STOP
  960. ::-- IF there is a second file name, assume it is a OBJ file,
  961. ::-- and link it to the %1 name.
  962.         IF X%2 == X GOTO ELSELINK
  963.                 ECHO link %1+%2;
  964.                 link %1+%2;
  965.                 GOTO ENDIFLINK
  966.         :ELSELINK
  967.                 echo link %1;
  968.                 link %1;
  969.         :ENDIFLINK
  970. :STOP
  971.  
  972. and save this as asm86.bat.
  973.  
  974. All this does is  1) create an object file (+O), 2) suppress the creation of 
  975. the symbol table .sym(+S), and 3) copy the errors to a the filename.err instead 
  976. of writing in your file(+E).
  977.  
  978. To assemble the message.asm with the batch file,  type
  979.   asm86 message
  980.  
  981. If there were any errors, you will have to edit the asm file to fix them. The
  982. error messages displayed by the assembler should indicate the line number and
  983. cause of the problem. Since you are just copying pregenerated code, any errors
  984. will simply be typos.
  985.  
  986. Once all of the errors have been corrected, a pair of files will have been
  987. created. The will have the same base name as the original asm file, but will
  988. have different extensions:
  989.  
  990. OBJ- Object file. Contains the basic machine code, but does not have any
  991. references to external procedures. This is, effectively, an intermediate file
  992. which is used by the linker to produce the final executable file.
  993.  
  994. EXE- Executable file. All external references resolved. Completely runable.
  995.  
  996. To run the program just type Message and you will see the line appear on the
  997. screen.
  998.  
  999. This was a simple Hello program. What you probably want is another example or 
  1000. two to try out, and that is what we shall do. The next Program that won't be as 
  1001. long but will have plenty of info.
  1002.  
  1003.  
  1004. CharLoop Program
  1005. ----------------
  1006. In this part, you will create a simple program that asks the user to enter a
  1007. character, and prints it out again. It does this repeatedly, until the user
  1008. hits the ESC key. Dos funtions 01H and 02H are introduced with this program,
  1009. and it is the first program containing a comparison loop.
  1010.  
  1011. Again you should start by copying template.asm to a file called charloop.asm.
  1012. Edit the charloop.asm template so that it has the following changes:
  1013.  
  1014. Create two messages by adding the following lines to the Data Segment part of
  1015. the program (see the message program instructions, if you don't remember how
  1016. to do this):
  1017.  
  1018. prompt    db    0DH, 0AH,"Enter a Character: $"
  1019. outmsg    db    0DH, 0AH, "You Entered:   $"
  1020.  
  1021. Now add the code which will put the following "pseudocode" into effect:
  1022.         Repeat
  1023.                 prompt for and read a character
  1024.                 Print the character back out with a message
  1025.         While the character read is not esc
  1026.  
  1027. Which will turn out to be the following assembly code:
  1028.  
  1029. char_loop:
  1030.         ;Print the prompt
  1031.         mov     dx, offset prompt
  1032.         mov     ah, 09H
  1033.         int     21H
  1034.         ;Read a character into AL
  1035.         mov     ah, 01H   ;(01H - with echo; 08H - no echo)
  1036.         int     21H
  1037.         mov     bl, al    ;save character in BL
  1038.         ;Print the final message
  1039.         mov     dx, offset outmsg
  1040.         mov     ah, 09H
  1041.         int     21H
  1042.         ;Write the character to the screen
  1043.         mov     dl, bl  ;put character in dl
  1044.         mov     ah, 02H
  1045.         int     21H
  1046.         ;Loop back, only if the character was not esc (1BH)
  1047.         cmp     bl, 1BH
  1048.         jne     char_loop
  1049.         ;End Repeat
  1050.  
  1051. Note how the two new DOS interrupts are called. The Function number is always
  1052. placed in AH before calling, and the INT 21H instruction is used to invoke the
  1053. interrupt. For Function 1H, which reads a character to the screen, the DL
  1054. register must be initialized with the appropiate value.
  1055.  
  1056. Note also that the character must be stored somewhere throughout the whole
  1057. loop, and it can NOT be stored either AL or DL -- AL is modified by Function
  1058. 02H, and DL is modified when DX is set to the address of teh strings. So BL is
  1059. used to store the character, and the value must be transferred between AL, BL
  1060. and DL during processing. This kind of juggling happens often in assembly
  1061. programming. Get this program running to watch another good program going ;-).
  1062.  
  1063.  
  1064. CharLoop Program without Echo
  1065. -----------------------------
  1066. In CharLoop program above. Function 01H was used to read a character from the
  1067. keyboard. It does more than just read a character, it also echoes it back to 
  1068. the screen. This way, when you type something, you get visual feedback of what 
  1069. you have done.
  1070.  
  1071. Function 08H works exactly the same as Function 01H, except for this echo
  1072. feature: Function 08H does NOT echo the character after reading it.
  1073.  
  1074. Create a new program which is exactly the same as CHARLOOP, except it should
  1075. use Function 08H to read the characters, instead of Function 01H. Write and
  1076. run the program to see how it works.
  1077.  
  1078.  
  1079. NumLoop Program
  1080. ---------------
  1081. This program will work in a similar fashion to the Charloop program above, but
  1082. it will read and print numbers. Since there is no DOS interrupt to convert
  1083. ASCII characters to numbers, your code will have to do this. Fortunately,
  1084. there are already procedures to do this. A few extra steps must be taken to
  1085. use them, but it will be much easier than writing the code from scratch. See
  1086. the info about DOSIOLT for details on how to use thes procedures.
  1087.  
  1088.                         DOSIOLT Procedures
  1089. Here is a description of the DOSIOLT procedures:
  1090.  
  1091. inhex16
  1092. This procedure reads a HEX number in character format from the standard input, 
  1093. and converts it to a word. Spaces or Tabs may precede or follow then number. 
  1094. DOS int 21H-0AH is used to read the input string, so it must be terminated by a 
  1095. RETURN. Both upper and lower case letters A-F may be used. If the number typed 
  1096. is larger than FFFH, the upper bits are lost. If anything unpredictable is 
  1097. typed(like non-HEX chars) the function will return junk.
  1098. Inputs:   None
  1099. Outputs:  AX- the word-sized number read.
  1100. Modifies: AX, flags
  1101.  
  1102. outhex16
  1103. This simple routine prints the four 'nibbles' of AX as ASCII digits.
  1104. Four digits are always printed.
  1105. Input: AX- the number to be printed
  1106. Outputs: None
  1107. Modifies: Flags
  1108.  
  1109. outHex8
  1110. This simple routine prints the two 'nibbles' of AL as ASCII digits. Two digits 
  1111. are always Printed.
  1112. Input: AL-the number to be printed
  1113. OUTPUT: NoneModifies: Flags
  1114.  
  1115. Call
  1116. Each of these procedures is invoked with the CALL instruction. Any
  1117. inputs(registers) must be initialized before the call; any outputs(also
  1118. registers) are set by the procedure, and contain the appropriate value after
  1119. the call.
  1120.  
  1121. For example, to print the 1-byte value "2F" to the screen:
  1122.         mov al, 2FH
  1123.         call outhex8  ;Prints: 2F
  1124. To Print "2AC5"
  1125.         mov ax, 2AC5H
  1126.         call outhex16 ; print 2AC5
  1127. To read a number from the keyboard:
  1128.         call inhex16 ;The ax register now contains the number read
  1129.  
  1130. Extern
  1131. Since the code for these functions does NOT appear in your ASM file, two
  1132. special steps must be taken in creating your executable file. The first is to
  1133. declare the names of the procedures as external procedures. This informs the
  1134. assembler that the code has been written elsewhere, and you didn't just forget
  1135. to write it.
  1136.  
  1137. The extern declaration should come someplace early in the ASM file. Although
  1138. it doesn't matter greatly where it goes, most programmers will put these
  1139. declarations outside of all of the segments. The template file given has a
  1140. spot for externals, marked with a commment.
  1141.  
  1142. The format for the declaration(in this case) is:  
  1143.   extern   procedure_name:far
  1144.         
  1145.         A86 USERS: The A86 Assembler uses the older version of the extern
  1146.         declaration, which is spelled extrn. If you are using the a86
  1147.         assembler(asm86.bat), make sure you spell the name of the instruction
  1148.         extrn.
  1149.  
  1150. procedure_name is the name of the procedure that you will use in the program.
  1151. The name only needs to be declared once in this way, no matter how many times
  1152. it is used. But if two or more DOSIOLT procedures are to be used, each must
  1153. have a separate declaration.
  1154.  
  1155. You should NOT place these extern declarations in your code unless you are
  1156. actually using the routines. The linker may place the code for the procedure
  1157. in your final executable even if it is never called.
  1158.  
  1159. LINKING
  1160. A special step must be taken in linking (the second half of the compilation
  1161. phase done by asm86.bat) to link the code in DOSIOLT. Fortunately, asm86.bat
  1162. can handle the extra file fairly automatically. Just include the DOSIOLT on
  1163. the command line, after your asm file name.
  1164.  
  1165. Example: assuming you have written a program in a file called "calc.asm" which
  1166. contains calls to the DOSIOLT procedures. To assemble and link the program:
  1167.   A:\> asm86 calc dosiolt
  1168. If you get an "Undefined Symbol" error, it is because you mistyped, or
  1169. forgot, the extern declarations for the DOSIOLT procedures. Make sure
  1170. these are correct.
  1171.  
  1172. If you get an "Unresolved External" error, it is because you forgot to put
  1173. "DOSIOLT" as the second file name; i.e. you typed: asm86 calc instead of
  1174. asm86 calc dosiolt.
  1175.  
  1176. This program will illustrate the use of two of the DOSIOLT procedures, and also
  1177. the use of variables, rather than registers, as places to store information.
  1178. The outline of the program is as follows:
  1179.          Loop forever
  1180.                 Prompt for, and read a number into the variable NUMBER
  1181.                 IF number = 0, then break out of the loop
  1182.                 print Number, with an appropriate announcement.
  1183.          End Loop
  1184.  
  1185. Your program will need a prompt string, a response string and a word-sized
  1186. variable in the Data Segment:
  1187.  
  1188. prompt1   db   0DH, 0AH, "Enter a number:  $"
  1189. response  db   0DH, 0AH, "You Entered:    $"
  1190. number    dw   ?
  1191.  
  1192. Number has been declared as a word-sized variable, with no initial value. The
  1193. Code can now use the name "Number" just like a register name( in most cases).
  1194.  
  1195. The code for the program is:
  1196.  
  1197. number_loop:
  1198.         ;Print the first prompt
  1199.         mov     dx, offset prompt1        mov
  1200.      ah, 09H
  1201.         int     21H
  1202.  
  1203.         ;Read a number into AX and put it in NUMBER
  1204.         call    inhex16        mov     number, ax
  1205.  
  1206.         ;If number = 0 the exit the loop
  1207.         cmp    number, 0H
  1208.         je     end_number_loop
  1209.  
  1210.         ;Print The second prompt.
  1211.         mov     dx, offset response
  1212.         mov     ah, 09H
  1213.         int     21H
  1214.         ;Print the number
  1215.         mov     ax, number
  1216.         call    outhex16
  1217.         jmp     number_loop
  1218. end_number_loop:
  1219.  
  1220. Note that the inhex16 reads a number into AX, and outhex16 prints the number
  1221. AX, yet this code went through all the trouble of storing the number in the
  1222. variable, rather than just leaving it in AX throughout the loop. WHY?!?
  1223. Because AX was needed in between the reading and printing of the code. Again,
  1224. this kind of juggling between registers and variables occurs often in assembly
  1225. programming.
  1226.  
  1227. Since two DOSIOLT procedures are being used, they must be declared. At the top
  1228. you will find the EXTERN part of your program template; add these lines to the
  1229. section:
  1230. ;===============================Extern======================================
  1231. extrn  inhex16:far
  1232. extrn  outhex16:far
  1233.  
  1234. Those are all the changes needed.
  1235. Don't forget to include the DOSIOLT file on the command line when compiling, 
  1236. which will be --- asm86 numloop dosiolt
  1237.  
  1238. I do apologize for the length of this but I got to excited when I was messing
  1239. with these old files and playing with these procedures in dosiolt.obj file.
  1240.  
  1241. If you want to try to use these files, you can email me at 
  1242.   linuxjr@hotmail.com
  1243. and request the dosiolt.obj to use with the numloop;  I will be more than happy
  1244. to send it.
  1245.  
  1246.  
  1247. ::/ \::::::.
  1248. :/___\:::::::.
  1249. /|    \::::::::.
  1250. :|   _/\:::::::::.
  1251. :| _|\  \::::::::::.
  1252. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  1253.                                                      Using the Gnu AS Assembler
  1254.                                                      by mammon_
  1255.  
  1256.  
  1257. Using the Gnu AS Assembler
  1258. mammon_
  1259.  
  1260. GAS is the GNU project port of the Unix AS assembler; it is available as part
  1261. of the binutils package which is included with any of the GNU compilers (for
  1262. example, GCC). GAS support is built into the various GNU compilers, and so GAS
  1263. can be invoked by invoking the compiler on a .S (asm source) file; however it
  1264. can also be run on any source file (for example, .asm files) by using the 'as'
  1265. command.
  1266.  
  1267. The GAS documentation is available on Linux installations in info (.gz) format,
  1268. and is viewed using the command 'info as' or 'info -f as.info'. For the
  1269. novice, a crash course in info: Info files are designed in a tree structure,
  1270. with each page or section being considered a 'node'; h gets help, q quits
  1271. info, SPACE scrolls down the screen, DEL scrolls up the screen, b jumps to the
  1272. beginning of the node, e jumps to the end of the node, n jumps to the next
  1273. node, p jumps to the previous node, g jumps to a specified node, m jumps to a
  1274. specified menu item, s searches the info file, and l steps back 1 node.
  1275.  
  1276. The sections of the most interest in the manual will be the Directives 
  1277. ('g Pseudo Ops'), Symbols ('g Symbols'), Constants ('g Constants'), and 
  1278. Sections ('g Sections') nodes. For more immediate references, the Intel 386-
  1279. specific topics can be consulted: 'g i386-Syntax', 'g i386-Opcodes', 
  1280. 'g i386-Regs', 'g i386-prefixes', 'g i386-Memory', 'g i386-jumps'.
  1281.  
  1282.  
  1283. The AT&T Syntax
  1284. ---------------
  1285. GAS uses the AT&T syntax, which is known to be confusing for those used to the 
  1286. Intel assembler syntax. It has been said that the AT&T syntax is less ambiguous 
  1287. than the Intel, and thus it has its own appeal.
  1288.  
  1289. Registers
  1290. One of the most obvious differences in syntax is that the registers in the AT&T
  1291. syntax are prefixed with %. Thus, 'eax ax al ah' would be written '%eax %ax %al
  1292. %ah' for GAS.
  1293.  
  1294. Opcode Format and Order
  1295. Unlike the Intel syntax which uses the format 'opcode dest, src', AT&T syntax 
  1296. uses the format 'opcode src, dest'; thus the command 'mov eax, ebx' in Intel 
  1297. would be 'mov %ebx, %eax' in AT&T. In addition, the opcodes in AT&T syntax all 
  1298. take suffixes to specify the size of the operand (note that these suffixes can 
  1299. be ignored usually, as GAS will guess the operand size by the size of the 
  1300. register being accessed)-- thus one would add 'w' to an opcode to specify a 
  1301. word operand, 'b' to specify a byte operand, and 'l' to specify a long operand. 
  1302. The Intel 'mov' opcode would then be specified in AT&T syntax by using 'movb', 
  1303. 'movw', or 'movl' as circumstances warrant. Note that this carries over into 
  1304. far calls; as the 'FAR" keyword is not present in GAS, one must prefix (not 
  1305. suffix) the call or jump with "l": thus a 'far call' becomes 'lcall', 'far 
  1306. jmp' becomes 'ljmp', and 'ret far' becomes 'lret'.
  1307.  
  1308. Immediate and Absolute values
  1309. Immediate values are prefixed with a $ in the AT&T syntax, while in the Intel 
  1310. syntax they are unmarked. Thus a 'push 4' statement becomes a 'push $4' in 
  1311. AT$T. Also, an absolute value is prefixed by a *, while in Intel it would be 
  1312. unmarked.
  1313.  
  1314. Memory Referencing
  1315. This is the part that is most likely to cause trouble for those used to the
  1316. Intel syntax. Intel uses the following syntax for memory references:
  1317. SECTION:[BASE + INDEX*SCALE + DISP]
  1318. where BASE is the register used as a base in the reference, INDEX is a register 
  1319. used to calculate an offset, SCALE is the multiplier used to calculate the 
  1320. offset from the INDEX register, and DISP is the displacement from the BASE or 
  1321. INDEX register. Some examples from the GAS manual:
  1322. [ebp - 4]       [BASE DISP]     (Note: DISP is -4)
  1323. [foo + eax*4]   [DISP + INDEX*SCALE] 
  1324. [foo]           [DISP]          (Value pointed to by 'foo')
  1325. gs:foo          SECTION:DISP    (Contents of variable 'foo')
  1326. AT&T syntax uses the following syntax for memoory references:
  1327. SECTION:DISP(BASE, INDEX, SCALE)
  1328. As with the Intel syntax, all of these are optional (and it appears that BASE 
  1329. and INDEX are rarely used together). The GAS manual provides the following 
  1330. examples equivalent to the above Intel examples:
  1331. -4(%ebp)        DISP(BASE)
  1332. foo(,%eax,4)    DISP(,INDEX,SCALE)
  1333. foo(,1)         DISP(,SCALE)        (Note: the single comma is intentional)
  1334. %gs:foo         SECTION:DISP
  1335. Note that you must provide commas within the parentheses whenever you skip an 
  1336. element (e.g., if you do not use BASE).
  1337.  
  1338. To illustrate, here are some examples of memory references mixed in with asm
  1339. opcodes (from http://www.castle.net/~avly/djasm.html):
  1340.         __AT&T______________________    __Intel_________________________
  1341.         movl 4(%ebp), %eax               mov eax, [ebp+4])
  1342.         addl (%eax,%eax,4), %ecx         add ecx, [eax + eax*4])
  1343.         movb $4, %fs:(%eax)              mov fs:eax, 4
  1344.         movl _array(,%eax,4), %eax       mov eax, [4*eax + array])
  1345.         movw _array(%ebx,%eax,4), %cx    mov cx, [ebx + 4*eax + array])
  1346.  
  1347.  
  1348. Labels & Symbols
  1349. Labels in GAS are the same as in other assemblers: the name of the label 
  1350. followed by a colon. All symbol names must begin with a letter, a period, or an 
  1351. underscore. Local symbols are defined using the digits 0-9 followed by a colon,
  1352. and are referred to using that digit followed by a b (for a backward reference) 
  1353. or f (for a forward reference); note that this allows only 10 local symbols. A 
  1354. symbol can be assigned a value using the equals sign (e.g. 'TRUE = 1') or by 
  1355. using the .set or .equ directives.
  1356.  
  1357.  
  1358. Directives
  1359. ----------
  1360. GAS allows most of the standard assembler directives; what follows are the most 
  1361. commonly used.
  1362.  
  1363. .align
  1364. Pad the section to a specified alignment (e.g. 4 bytes); this directive takes 
  1365. as an argument the alignment sized, as well as an optional argument specifying 
  1366. the byte used to fill the pad areas (default is 00).
  1367.  
  1368. .ascii, .asciz, .string
  1369. Each of these directives takes one or more strings separated by commas; in the 
  1370. .ascii directive, the strings are not terminated, in the .asciz and .string 
  1371. directives the strings are zero-terminated.
  1372.  
  1373. .byte, .double, .int, .word
  1374. Each of these directives takes as an argument an expression (for example, 
  1375. value1 + value2) and defines the specified number of bytes (byte, int, word, 
  1376. etc) at the current location to the result of the expression.
  1377.  
  1378. .data, .section, .text 
  1379. The .section directive allows segments or sections of the target program to be 
  1380. defined for the linker. The .section directive takes a section name, as well as
  1381. section flags (b = bss, w = writable, d = data, r = read-only, x = executable 
  1382. for COFF files; a = allocatable, w = writable, x = executable, @progbits =  
  1383. data, @nobits = no data for ELF files). The .data and .text directives are 
  1384. pre-defined .section directives for data and code sections.
  1385.  
  1386. .equ, .set 
  1387. Each of these sets the first argument (a symbol) with the result of the second 
  1388. argument (an expression), for example
  1389. .equ TRUE 1
  1390. sets the Symbol TRUE to the value 1.
  1391.  
  1392. .extern 
  1393. The traditional EXTERN directive is available but ignored; GAS treats all 
  1394. undefined symbols as externs.
  1395.  
  1396. .global, .globl
  1397. These directives define global (exported) symbols; each takes as an argument 
  1398. the symbol to be made global.
  1399.  
  1400. .if /.endif
  1401. GAS provides the usual IF...ENDIF directives for conditional assembly; the .if 
  1402. directive is followed by an expression, and all code between the .if and the 
  1403. .endif directive is assembled only if that expression returns non-zero.
  1404.  
  1405. .include 
  1406. This directive includes a file at the current location; it takes as an argument 
  1407. the name of the file in quotes, for example
  1408. .include "stdio.inc"
  1409.  
  1410.  
  1411. Assembling a Program
  1412. --------------------
  1413. A GAS program can ge assembled by invoking GCC with the O2 (optimize: level 2) 
  1414. option. Note that all GAS programs must have a .text section and a global 
  1415. "main" label.
  1416.  
  1417. Here is an example of a 'hello world'-style program in GAS:
  1418. ; gashello.S ==========================================================
  1419. .text
  1420. message:
  1421. .ascii "Helloooo, nurse!\0"
  1422. .globl main
  1423. main:
  1424.     pushl $message
  1425.     call puts
  1426.     popl %eax
  1427.     ret
  1428. ; EOF =================================================================
  1429. This can be compiled with the command
  1430. gcc -02 gashello.S -o ghello
  1431. or with
  1432. as gashello.S -o gashello.o
  1433. ld -o gashello gashello.o -lc -s -defsym _start=main
  1434. Note that it is much easier to use GCC than to use AS, as you will have to 
  1435. explicitly specify the librarys to link to (hence the -lc parameter) when you 
  1436. call LD.
  1437.  
  1438. The Int80 "pid.asm" program from last month's Liux article would be written for 
  1439. GAS as follows:
  1440. ;pid.S====================================================================
  1441. .global main
  1442. .text
  1443. szText1:
  1444. .asciz "Getting Current Process ID..." 
  1445. szDone:
  1446. .asciz "Done!"
  1447. szError:
  1448. .asciz "Error in int 80!"
  1449. szOutput:
  1450. .string "%d\n"
  1451.  
  1452. main:
  1453.     pushl $szText1
  1454.     call puts
  1455.     popl %ecx
  1456.     mov $20, %eax
  1457.     int $128
  1458.     cmp $0, %eax
  1459.     je Error
  1460.     pushl %eax
  1461.      pushl $szOutput
  1462.     call printf
  1463.     popl %ecx
  1464.     popl %ecx
  1465.     pushl $szDone
  1466.     call puts
  1467.     jmp Exit
  1468. Error:
  1469.     pushl $szError
  1470.     call puts
  1471. Exit:
  1472.     popl %ecx
  1473.     ret
  1474. ; EOF ====================================================================
  1475. This can be compiled in the same manner as the previous example; note, though, 
  1476. the need to use decimal numbers when calling interrupts (the 0x?? syntax for 
  1477. specifying a hexadecimal integer causes the opcode to not be recognized by the 
  1478. assembler).
  1479.  
  1480.  
  1481. ::/ \::::::.
  1482. :/___\:::::::.
  1483. /|    \::::::::.
  1484. :|   _/\:::::::::.
  1485. :| _|\  \::::::::::.
  1486. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  1487.                                                 A Guide to NASM for TASM Coders
  1488.                                                 by Gij
  1489.  
  1490.  
  1491. Generalities
  1492. ------------
  1493. The basic function of any assembler it to turn asm into the equivalent binary 
  1494. code file; that's true for TASM, NASM, and any other assembler.
  1495.  
  1496. The differences arise in the special features each assembler offers you. For 
  1497. example, the MODEL directive exists in TASM, making it easier for the coder to 
  1498. reference data variables in other segments. NASM does not have an equivalent 
  1499. directive, so you have to keep track of the segment registers yourself, and put 
  1500. segment overrides where they are needed. This does not mean that NASM doesn't 
  1501. have good SEGMENT or GROUP support; in fact it has both, though they are not 
  1502. quite the same as in TASM.
  1503.  
  1504. It's a different way of coding, and it may seem to require more work, but after 
  1505. you get used to it it's easier, because you know exactly what's going on in 
  1506. your code. NASM actually gives you the closest possible idea of what your asm 
  1507. source code will become once it's compiled.
  1508.  
  1509. TASM is chock-full of directives; looking at a small reference for TASM 4.0,
  1510. there are at least a few dozen directives TASM uses, and you have to know
  1511. quite a bit of them by heart. NASM on the other hand has very few directives. 
  1512. Actually, you can write an asm file that will assemble just fine without using 
  1513. a single directive, although I doubt it will be useful in most cases.
  1514.  
  1515. NASM is also less ambivalent towards syntax, which leaves less room for
  1516. software bugs, but makes it more strict when assembling. I actually think NASM 
  1517. is easier to learn then TASM since it's much more straight-forward.
  1518.  
  1519. Your NASM Bible is of course the accompanying docs, you can get them in a 
  1520. separate package from the same place you got the binaries for NASM. All in all 
  1521. I think you will find NASM to be just as capable as TASM if not more so. 
  1522. Although it's missing some features TASM has, you can always mail the author 
  1523. and ask for a feature, and you just might get lucky when the new version comes out.
  1524.  
  1525. ASM code is usually the same in any assembler ( AT&T syntax is an exception )
  1526. but there are a few subtleties that TASM coders should look out for. The docs 
  1527. that accompany NASM have a nice list of them, and I'll mention the most 
  1528. significant ones here.
  1529.  
  1530.  
  1531. DATA offset vs DATA contents
  1532. ----------------------------
  1533. TASM uses this syntax to move
  1534.     mov esi, offset MyVar
  1535.    OR
  1536.     lea esi, MyVar
  1537. LEA is used to load complex offsets like "[esi*4+ebx]" into a register. TASM
  1538. supports LEA even when used with a simple offset like "Myvar".
  1539.  
  1540. NASM on the other hand only supports one way of loading a simple offset into a
  1541. register (the LEA form is only valid when using complex offsets):
  1542.     mov esi, MyVar
  1543. This ALWAYS means move the offest of MyVar into esi.
  1544.  
  1545. On the other hand, This:
  1546.     mov eax, [MyVar]
  1547. Will always mean move the contents of MyVar into eax.
  1548.  
  1549. However, using LEA to load a complex offset is valid in both TASM and NASM:
  1550.     lea edi,[esi*4+EBX]    ; valid in both assemblers
  1551.  
  1552. NASM also support a SEG keyword:
  1553.     mov ax,SEG MyVar
  1554. This moves the segment of the variable into ax.
  1555.  
  1556.  
  1557. Segment Overrides
  1558. -----------------
  1559. TASM is more lax in it's syntax, so both of these are valid code:
  1560.     mov ax,ds:[si]
  1561. AND
  1562.     mov ax,[ds:si]
  1563.  
  1564. NASM doesn't allow this--if you specify a variable inside the square brackets
  1565. all of the specifiers should be inside the square brackets.
  1566. So this is the only valid option:
  1567.     mov ax,[ds:si]
  1568.  
  1569.  
  1570. Specifying operand size
  1571. -----------------------
  1572. TASM coders usually have lexical difficulties with NASM because it lacks the 
  1573. "ptr" keyword used extensively in TASM.
  1574.  
  1575. TASM uses this:
  1576.     mov al,  byte ptr [ds:si]
  1577. OR
  1578.     mov ax,  word ptr [ds:si]
  1579. OR
  1580.     mov eax, dword ptr [ds:si]
  1581.  
  1582. For NASM This simply translates into:
  1583.     mov al,  byte [ds:si]
  1584. OR
  1585.     mov ax,  word [ds:si]
  1586. OR
  1587.     mov eax, dword [ds:si]
  1588.  
  1589. NASM allows these size keywords in many places, and thus gives you a lot of 
  1590. control over the generated opcodes in a uniform way. For example, the following 
  1591. are all valid:
  1592.     push dword 123
  1593.     jmp  [ds: word 1234]   ; these both specify the size of the offset
  1594.     jmp  [ds: dword 1234]  ; for tricky code when interfacing 32bit and
  1595.                            ; 16bit segments
  1596.  
  1597. It can get pretty hairy with operand size being this final, but the important 
  1598. thing to remember is you can have all the control you need, when you want it.
  1599.  
  1600.  
  1601. Functions
  1602. ---------
  1603. TASM has special directives for declaring a procedure and ending it. Why?
  1604. A procedure is just another code label you CALL instead of JMP--NASM got it
  1605. right.
  1606.  
  1607. TASM uses:
  1608. ProcName PROC
  1609.     xor ax,ax
  1610.     ret
  1611. ProcName ENDP
  1612.  
  1613. while NASM just uses:
  1614. Procname:
  1615.     xor ax,ax
  1616.     ret
  1617.  
  1618. To declare a procedure PUBLIC, just use the GLOBAL directive:
  1619. GLOBAL Procname
  1620. Procname:
  1621.     xor ax,ax
  1622.     ret
  1623.  
  1624.  
  1625. Local Labels
  1626. ------------
  1627. Those of you that know C also know that a member of a struct can be referenced
  1628. as StructInstance.MemberName. This is rather similar to the way NASM allows
  1629. you to use local labels. A Local Label is denoted by prefixing a dot to the 
  1630. label name:
  1631.  
  1632. Label1:
  1633.     nop
  1634. .local:
  1635.     nop
  1636. Label2:
  1637.     nop
  1638. .local:
  1639.     nop
  1640.  
  1641. This won't give you an error on multiple definitions of label, but you can
  1642. still jmp to a certain label like this:
  1643.     jmp Label2.local
  1644. ...so it's local, and in a way it's also a global label.
  1645.  
  1646.  
  1647. ORG Directive
  1648. --------------
  1649. NASM supports the org directive, so if you are coding a COM file you can start 
  1650. with:
  1651.     org 0x100h
  1652. OR
  1653.     org 100h
  1654. (NASM allows both the asm and c methods of specifying hex, so both of the
  1655. above are valid.)
  1656.  
  1657.  
  1658. Reserving Space
  1659. ---------------
  1660. Once again, here NASM uses a different syntax then that of TASM.
  1661.  
  1662. In TASM you would declare a 100 bytes of uninitialized space like this:
  1663.     Array1: db 100 dup (?)
  1664.  
  1665. NASM uses its own keywords to do this; these are RESB, RESW and RESD,
  1666. standing for REServeByte, REServeWord, and REServeDword, respectively.
  1667. To reserve 10 bytes, you would use RES? keywords like this:
  1668.     Array1: RESB 100
  1669. OR
  1670.     Array1: RESW 100/2
  1671. OR
  1672.     Array1: RESD 100/4
  1673.  
  1674. Declaring initialized space is much like TASM, but arrays are different.
  1675. In TASM:
  1676.     Array1: db 100 dup (1)
  1677. In NASM:
  1678.     Array1: TIMES 100 db 1
  1679.  
  1680. TIMES is a handy little directive, it instructs NASM to preform an action
  1681. a specified number of times, in the example above I preform "db 1" a 100
  1682. times. TIMES can be used for virtually anything; for example:
  1683.     TIMES 69 nop
  1684. will put 69 nops at the current point in the file.
  1685.  
  1686. The $ (current location) symbol is supported by NASM, and can be used to 
  1687. specify the 'count' operand to TIMES, so this is valid:
  1688.  label1:
  1689.     mov ax,1
  1690.     xor ax,ax
  1691.  label2:
  1692.       TIMES $-label1 nop
  1693. This expands to TIMES (label2 - label1), and will put as many one-byte nops 
  1694. after label2, as the byte count between label1 and label2.
  1695.  
  1696.  
  1697. Making Structs
  1698. --------------
  1699. I fought long and hard to get structs going, the docs were a bit vague, and
  1700. it took a while to get it, here it is.
  1701.  
  1702. Using a struct is divided into 2 parts, declaring the prototype, and making an
  1703. instance. A simple, 2-member structure would be defined as follows:
  1704.     struc st
  1705.         stLong resd 1
  1706.         stWord resw 1
  1707.     endstruc
  1708.  
  1709. this declares a prototype struct named st, with 2 members, stLong which is a
  1710. DWORD, and stWord which is a word. It uses the reserve directives because it's 
  1711. a prototype, not a real struct. You can use istruc to make a real instance that
  1712. you can reference as data in your code:
  1713.  
  1714. mystruc:
  1715.     istruc st
  1716.         at stLong, dd 1
  1717.         at stWord, dw 1
  1718.     iend
  1719. *Note: it's important to put the label on a different line.
  1720.  
  1721. This creates a struct named mystruc of type st; the "at" keyword is used to 
  1722. assign initial values to the members of the struc (i.e., at the reserverd bytes 
  1723. of memory).
  1724.  
  1725. The notation for referencing members is not like in C. This is because of the
  1726. way structures are implemented; in NASM, each member is assigned an offset 
  1727. relative to the beginning of the struct:
  1728.  
  1729. mystruc:
  1730.     istruc st
  1731.         at stLong, dd 1  ; offset 0
  1732.         at stWord, dw 1  ; offset 4
  1733.     iend
  1734.  
  1735. The notation for referencing a member is therefore:
  1736.     mov eax, [mystruc+stLong]
  1737.  
  1738. This is because mystruc is a constant base, and the member is a relative offset
  1739. to it. It's similar to referencing a data array.
  1740.  
  1741. One thing I should mention: If you declare structs prototypes as above, the
  1742. member names/labels will be global, so you will get collisions if you use the
  1743. same member name in your code or in another struct prototype. To avoid this, 
  1744. precede the member names with a dot '.', and then reference them in relation to 
  1745. the prototype's name in the instance declaration. For example:
  1746.  
  1747.     struc st
  1748.         .stLong resd 1
  1749.         .stWord resw 1
  1750.     endstruc
  1751. mystruc:
  1752.     istruc st
  1753.         at st.stLong, dd 1
  1754.         at st.stWord, dw 1
  1755.     iend
  1756.  
  1757. And this is how you reference the members in code:
  1758.     mov eax,[mystruc+st.stWord]
  1759.  
  1760. This may seem confusing; you should understand that "mystruc" is the base of a
  1761. particular instance, and "st.stLong" is an offset relative to the start of the
  1762. struct, so in pseudo-code it translates into:
  1763.     mov eax,[offset mystruc + (offset stWord-offset start_of_proto]
  1764. or
  1765.     mov eax,[offset mystruc + 4]
  1766. ...which gives you the correct offset for the stWord member of the "mystruc"
  1767. struct instance.
  1768.  
  1769.  
  1770. Using Macros
  1771. ------------
  1772. This is a large part of the nasm docs, and a bit too much to get into in depth
  1773. here. I'll try and cover the major issues.
  1774.  
  1775. There are 2 types of macros, one-line and multi-line, all macro keywords are
  1776. preceeded with a '%' character.
  1777.  
  1778. An example of a single-line macro:
  1779.   %define mul(a,b) (a*b)
  1780.  
  1781. ...which would be reference in the source code as follows:
  1782.     mov eax,mul(2,3)
  1783.  
  1784. This will be converted into:
  1785.     mov eax,6
  1786.  
  1787. You can invoke other macros from within a macro:
  1788. %define fancymul(a,b) ( a * triple_mul(4) )
  1789. %define triple_mul(a) (a*3)
  1790.     mov eax,fancymul(2,3)
  1791.  
  1792. This becomes:
  1793.     mov eax, ( 2 * ( 3 * 4 ) )
  1794.  
  1795. These are not very useful examples, but i'm sure you can see the potential.
  1796.  
  1797. Multi-Line macros are much the same as single-line macros, but the syntax
  1798. is a bit different:
  1799.   %macro name number_of_args
  1800.      <body of macro>
  1801.   %endmacro
  1802.  
  1803. So, for example, if you wanted to make a small asm effort-saver you could write
  1804. the following macro:
  1805.  %macro prologue 1
  1806.     push ebp
  1807.     mov ebp,esp
  1808.     sub esp,%1
  1809.  %endmacro
  1810. ...and then you can use it in your code like this:
  1811.  
  1812. DemoFunc:
  1813.     prologue 4*2
  1814.     <body of function>
  1815.  
  1816. This would set up a stack frame and reserve room for 2 DWORD local variables.
  1817. You'll notice that args supplied to the macro can be referenced as %1....%n, 
  1818. similar to DOS and Unix shell/batch programming.
  1819.  
  1820. This is just a quick taste, there's more to be learned about NASM macros: the 
  1821. docs are your friends.
  1822.  
  1823.  
  1824. Includes
  1825. --------
  1826. Including files is easy, If you want to include .inc's into your asm file
  1827. you can use:
  1828.     %include "win32.inc"
  1829.  
  1830. If you wish to include binary files, you must use a different keyword:
  1831.     INCBIN     "data.bin"
  1832.  
  1833.  
  1834. Conditional Assembly
  1835. --------------------
  1836. NASM also has support for conditional assembly:
  1837. %define INCLUDE_WIN32_INCS
  1838. %ifdef  INCLUDE_WIN32_INCS
  1839.     %include "win32.inc"
  1840.     %include "toolhelp.inc"
  1841.     %include "messages.inc"
  1842. %endif
  1843.  
  1844. This way you can control the inclusion of files defining on the command line:
  1845.     "nasmw -dINCLUDE_WIN32_INC"
  1846. or by commenting out the %define line. The body of the %ifdef will be processed
  1847. only if a macro/define named INCLUDE_WIN32_INCS is defined.
  1848.  
  1849.  
  1850. Externs, Globals and Commons
  1851. -----------------------------
  1852. When Coding a multi-source-files project,  writing a dll, or calling API
  1853. functions you need to declare various symbols/data/functions a certain type
  1854. to make them available to the Assembler and you.
  1855.  
  1856. There are 3 types of symbols in NASM: EXTERN, GLOBAL and COMMON. Their 
  1857. invocation is all the same:
  1858.     EXTERN symbol_name      ; use this to define API calls for use
  1859.     GLOBAL symbol_name
  1860.     COMMON symbol_name
  1861.  
  1862. They all must appear before the actual symbol is defined/referenced. If you 
  1863. have experience in asm/c, their use should be clear -- EXTERN declares an 
  1864. external reference ofr the linker to resolve (an "import"), GLOBAL declares a 
  1865. symbol to be globally/publicly available (an "export"), and COMMON declares a 
  1866. variable to be of Common data type (i.e., all instances of a COMMON variable 
  1867. are merged into a single instance during compilation).
  1868.  
  1869. NASM 0.97 also has IMPORT/EXPORT extensions to the .obj format, for writing 
  1870. DLL's; read the docs for more info.
  1871.  
  1872.  
  1873. Specifying Segment Type
  1874. -----------------------
  1875. You can declare segments much the same as you would in TASM:
  1876.     segment .data use32 CLASS=data
  1877. or
  1878.     segment .text use32 CLASS=code
  1879. or
  1880.     segment Gij use16 CLASS=code
  1881.  
  1882. This is a good way to set segments straight for linking. Note that Nasm does 
  1883. not require certain segments to be present: you have full control over the 
  1884. segmentation of the program.
  1885.  
  1886.  
  1887. Output Formats
  1888. --------------
  1889. Nasm supports a plethora of output formats; depending on what you are trying
  1890. to accomplish, you should read the docs for special extensions to each type.
  1891. The output format is chosen using "nasm -f type" on the command line, where 
  1892. type can be bin, obj, win32 and others.
  1893.  
  1894. Each linker likes different formats--tlink likes obj for example, while 
  1895. LCC-WIN32 likes the win32 format...investigate on your own to find the best 
  1896. output format for your linker.
  1897.  
  1898. *tip: when assembling into the "obj" type, make sure and use the special
  1899.       "..start:" symbol to specify the entry point for the file.
  1900.  
  1901.  
  1902. In Closing
  1903. ----------
  1904. That's all for now. This is intended to be a 'quick-start' guide for TASM 
  1905. coders who want --or need-- to move into NASM; it is not a substitute for the 
  1906. NASM documentation. If you need to reach me, my e-mail is gij <at> bigfoot.com
  1907. Enjoy NASM!
  1908.  
  1909.  
  1910. ::/ \::::::.
  1911. :/___\:::::::.
  1912. /|    \::::::::.
  1913. :|   _/\:::::::::.
  1914. :| _|\  \::::::::::.
  1915. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  1916.                                            Tips on Saving Bytes in ASM Programs
  1917.                                            by Larry Hammick
  1918.  
  1919.  
  1920. The programmer's word for craftsmanship is "optimization". This term refers to 
  1921. conservation, either of program size or execution time. It's time includes not 
  1922. just CPU clocks, but the time consumed by peripherals (e.g. disks, at load 
  1923. time) and by the operating system calls. This article is concerned with the 
  1924. conservation of size, or bytes. Size may refer either to the program file size, 
  1925. or to the size of the memory the program uses. The two are not always identical.
  1926.  
  1927. In all the illustrations, we assume that 16-bit code segments are involved. The 
  1928. syntax we use is that of MASM 5.1; the difference from other assemblers is 
  1929. slight.
  1930.  
  1931.  
  1932. 1. Avoid uninitialized data.
  1933. ---------------------------
  1934. An instruction like this:
  1935.     OutputHandle dw ?
  1936. is usually a waste of space. Depending on the memory model (i.e. depending on 
  1937. whether we have CS=DS, and the like), there are several ways to omit these two 
  1938. bytes from the program file and the memory image.
  1939.  
  1940. If DS is the PSP segment, use:
  1941.     OutputHandle equ word ptr ds:[5Ch]
  1942. or similar, for a value other than 5Ch. Any program may safely use any part of 
  1943. the PSP from 3Ch to 07Fh, plus the word at 2Ch (environment segment). When the 
  1944. program is finished with the command tail (bytes 80h-0FFh), it can reuse that 
  1945. area as well. Other parts of the PSP should not be modified, because they may 
  1946. be needed by DOS when the program exits. However, in the case of a TSR, the 
  1947. stay-resident part of the code (e.g. an interrupt handler) may use any part of 
  1948. the PSP after the TSR exit has been executed. In such cases, the PSP makes a 
  1949. handy buffer of 100h bytes with ORG 0.
  1950.  
  1951. If DS=CS, you can define uninitialized variables like this:
  1952.     OutputHandle    dw  ?
  1953.     InputHandle     dw  ?
  1954.     ORG             OutputHandle
  1955.     Go: mov ah,30h
  1956.         int 21h
  1957.         ...
  1958.         mov OutputHandle,ax
  1959.         ...
  1960.     END Go
  1961.  
  1962. or, equivalently:
  1963.     OutputHandle    equ     word ptr ds:[Go]
  1964.     InputHandle     equ     word ptr ds:[Go+2].
  1965.  
  1966. If DS is a dynamically allocated segment, or if it is part of the stack, there 
  1967. is this trick:
  1968.     OutputHandle    equ     word ptr ds:[0]
  1969.     InputHandle     equ     word ptr ds:[2].
  1970.  
  1971. Allocating file and memory space just for uninitialized variables wastes a few 
  1972. bytes here and there. Much worse, for file size, is to put whole buffers and 
  1973. stacks in the file:
  1974.     ReadBuffer      db  1000h   dup (0)
  1975.     Stack           db  40h     dup ("--Stack!--")
  1976. Examine a few commercial programs under a hex editor or debugger to see how 
  1977. common this practice is. Worldwide, the quantity of disk space thus wasted must 
  1978. be astronomical. Moreover, such "data" gets copied from disk every time the 
  1979. program is loaded, even though it has no meaning! Perhaps assemblers and 
  1980. linkers will someday be smart enough to avoid this. For now, we do have EXE 
  1981. packers such as PKLite to compress blank data blocks, but the latter can be 
  1982. avoided entirely as follows.
  1983.  
  1984. If DS is a dynamic segment or part of the stack:
  1985.     BufferSize      equ     1000h
  1986.     ReadBuffer      equ     0
  1987.     WriteBuffer     equ     ReadBuffer+BufferSize
  1988.     ...
  1989.         mov dx,ReadBuff ;rather than mov dx,offset ReadBuff
  1990.         mov ah,3Fh
  1991.         int 21h
  1992.     ...
  1993.  
  1994. If the program will be small enough for the code and all data to fit in one 
  1995. segment, it is desirable to have CS=DS. Then you can do:
  1996.     ReadBuffer      equ     offset EndOfCode
  1997.     WriteBuffer     equ     ReadBuffer+BufferSize
  1998.     Go:
  1999.         ...     ;code instructions
  2000.         mov ah,4Ch
  2001.         int 21h     ;exit
  2002.     EndOfCode label byte
  2003.     END Go
  2004.  
  2005. This practice is not quite safe for a COM program, because DOS will load a COM 
  2006. file into less than 64K if no larger block is available or if memory is 
  2007. fragmented. For an EXE, the EXE header can be adjusted to prevent the program 
  2008. from loading into too little memory.
  2009.  
  2010.  
  2011. 2. Put related data together.
  2012. ----------------------------
  2013. An example:
  2014.     CursorPosition  label   word
  2015.     CursorColumn    db      0Eh
  2016.     CursorRow       db      8
  2017.  
  2018. You will be able to load or save both variables with one instruction:
  2019.     mov dx,CursorPosition
  2020.  
  2021. Another benefit:
  2022.     and CursorPosition,0FF00h
  2023.     jnz NotAtTop
  2024.  
  2025. The AND instruction sets one byte and tests another, at the same time.
  2026.  
  2027.  
  2028. 3. Avoid forward references.
  2029. ---------------------------
  2030. Forward references in source can result in worthless NOP's getting assebled. 
  2031. This is another illustration of the principle that assemblers are pretty dumb. 
  2032.  
  2033. Consider:
  2034.     mov cx,MsgSize        ;(1)
  2035.     ...
  2036.     Msg         db      "Hello",0Dh,0Ah
  2037.     MsgSize     equ     $-offset Msg
  2038.  
  2039. MsgSize is a constant word. But MASM doen't know that when it assembles the 
  2040. instruction (1). So it provides 3 bytes for MsgSize, and later fills in the 
  2041. constant word followed by a NOP byte. One solution:
  2042.             db      0B9h     ;opcode for mov cx,immed
  2043.             dw      MsgSize
  2044.     ...
  2045.     Msg     db      "Hello",0Dh,0Ah
  2046.     MsgSize equ     $-offset Msg
  2047.  
  2048.  
  2049. 4. Use cheap opcodes.
  2050. --------------------
  2051.  
  2052. 4.1 XCHG AX,Reg16
  2053. These 8 instructions are each just 1 byte. Don't use either MOV AX,CX or 
  2054. MOV CX,AX unless you need the same value in both registers. AX is special in 
  2055. this respect; instructions such as XCHG BX,CX or XCHG SI,DI are two bytes. 
  2056. XCHG EAX,Reg32 is two bytes (in 16-bit code segments), whereas MOV EAX,ECX etc. 
  2057. is three.
  2058.  
  2059. 4.2 CBW, CDW, CDQ
  2060. To put AH=0, the instructions
  2061.     xor ah,ah
  2062.     sub ah,ah
  2063.     mov ah,0
  2064. occupy two bytes each. But if you know that AL > 0, the instruction CBW has the 
  2065. same effect (except that it leaves the flags unchanged) and is only one byte. 
  2066. Likewise, CWD can save over XOR DX,DX. CDQ is a 2-byte opcode but still better 
  2067. than XOR EDX,EDX, which is 3 bytes.
  2068.  
  2069. 4.3 JCXZ
  2070. This instruction does not require a preliminary flag-setting instruction. So, 
  2071. you might prefer
  2072.     xchg ax,cx
  2073.     jcxz Mylabel
  2074. to
  2075.     or ax,ax
  2076.     jz MyLabel,
  2077. saving one byte. Be aware that JCXZ is a relatively slow opcode.
  2078.  
  2079. 4.4 INC Reg16 and DEC Reg16
  2080. These 16 opcodes are just one byte each. The opcodes INC Reg8 and DEC Reg8 are 
  2081. 2-byte. So use INC CX instead of INC CL if there is no possibility of carry 
  2082. from CL into CH. If CX is known to be 0, INC CX saves a byte vs. MOV CL,1, and 
  2083. 2 bytes vs. MOV CX,1. Similar tricks apply to going from -1 to 0, to decrement-
  2084. ing from 1 to 0 or from 0 to -1.
  2085.  
  2086. 4.5 Prefer the accumulator to other registers.
  2087. The following opcodes, among others, are cheaper for AX or EAX than for other 
  2088. general registers.
  2089.     MOV reg,mem
  2090.     MOV mem,reg
  2091.     ADD reg,mem
  2092.  
  2093.  
  2094. 5. Be flexible on flow control.
  2095. ------------------------------
  2096. Block-structuring is very sensible in high-level languages, but in ASM it is 
  2097. little more than a pedantic habit. In ASM, a routine may have more than one 
  2098. entry point and more than one exit (RETN, RETF, or IRET). Several routines may 
  2099. share exit code or entry code. A routine need not return at all. A few examples 
  2100. of how this can save bytes:
  2101.  
  2102. 5.1 Discard return addresses that won't be needed.
  2103. This sort of thing appears often:
  2104.     Mysub:  cmp al,3
  2105.             ja StcRet
  2106.             ...
  2107.             ret
  2108.     StcRet: stc
  2109.             ret
  2110.  
  2111.             ...
  2112.             call MySub
  2113.             jc Ret1
  2114.             ...
  2115.     Ret1:   ret
  2116.  
  2117. Better is:
  2118.     Mysub:   cmp al,3
  2119.              ja DontRet
  2120.              ...
  2121.              ret
  2122.     DontRet: pop ax    ;discard return address into some unneeded register
  2123.              ret
  2124.              ...
  2125.              call MySub  ;returns only if input is okay
  2126.              ...
  2127.  
  2128. 5.2 Reuse exit code.
  2129. If you see this more than once in your source:
  2130.     pop bx
  2131.     pop dx
  2132.     pop ax
  2133.     retn,
  2134. make a label at POP BX, and use a jump to that label from each other occurrence.
  2135. If this happens more than once:
  2136.     push ax
  2137.     push cx
  2138.     push dx
  2139.     push bx
  2140. consider a subroutine:
  2141.     SaveRegs: pop si        ;store return address in an unneeded register
  2142.               push ax
  2143.               push cx
  2144.               push dx
  2145.               push bx
  2146.               jmp si
  2147.  
  2148. 5.3. Consider CALL instead of JMP.
  2149. The CALL instruction can be used instead of JMP to pass a near address at 
  2150. almost no cost.
  2151.                  mov ah,30h
  2152.                  int 21h
  2153.                  cmp al,5
  2154.                  jae EnoughDOS
  2155.                  call ErrExit
  2156.                  db "This program requires DOS 5+",13,10,0
  2157.     EnoughDOS:
  2158.     ...
  2159.     ErrExit:     pop si ;"Return address" actually points at data.
  2160.     ErrExitLoop: lodsb
  2161.                  or al,al
  2162.                  jz Exit
  2163.                  int 29h
  2164.                  jmp short ErrExitLoop
  2165.     Exit:        mov ax,4CFFh
  2166.                  int 21h
  2167.  
  2168. In the above example, the routine ErrExit writes an ASCIIZ string from CS:SI, 
  2169. then exits.
  2170.  
  2171. The offset of a jump table can sometimes be passed in the same way.
  2172.     call SmartJump    ;does not return
  2173.     db      3
  2174.     dw      Handle3  ;Handle3 and Handle7 are near code addresses
  2175.     db      7
  2176.     dw      Handle7
  2177.     db      0        ;terminator for the table
  2178.  
  2179.     SmartJump:    ;input is a jump table index AL.
  2180.                    pop di   ;"return address" actually points at the jump table
  2181.     SmartJumpLoop: cmp byte ptr[di],0
  2182.                    je NotFound
  2183.                    scasb
  2184.                    je Found
  2185.                    scasw       ;cheaper than incrementing di twice
  2186.                    jmp short SmartJumpLoop
  2187.     Found:         jmp word ptr es:[di]
  2188.     NotFound: ...
  2189. The above example assumes ES=CS.
  2190.  
  2191. 5.4 Short jumps are cheaper than near jumps.
  2192. You can often save a few bytes by arranging your source so that jumps are short 
  2193. rather than near.
  2194.  
  2195. If this occurs:
  2196.         cmp al,5
  2197.         jne Not5
  2198.         jmp CantRun
  2199.     Not5:
  2200.         ...
  2201.         jmp CantRun
  2202.         ...
  2203. and CantRun is not reachable by a short jump in either instance, you might 
  2204. still save a byte like so:
  2205.                 cmp al,5
  2206.                 jne Not5
  2207.     JmpCantRun: jmp CantRun
  2208.     Not5:
  2209.                 ...
  2210.                 jmp short JmpCantRun    ;2-step jump
  2211.                 ...
  2212.  
  2213.  
  2214. 6. Registers are cheaper than constants.
  2215. ---------------------------------------
  2216. You should never write this (6 bytes):
  2217.     mov si,StringSite        ;a 16-bit constant
  2218.     mov di,StringSite
  2219.  
  2220. Instead (5 bytes):
  2221.     mov si,StringSite
  2222.     mov di,si.
  2223.  
  2224. Another illustration:
  2225.     MyByte db 11h
  2226.     ...
  2227.     mov MyByte,0        ;a 5-byte instruction
  2228.     mov MyByte,bh        ;4 bytes, and equivalent if bh is known to be 0
  2229.     mov MyByte,al        ;only 3 bytes.
  2230.  
  2231.  
  2232. 7. Code can be used as data.
  2233. ---------------------------
  2234. Here are two examples of a slick technique known as self-modifying code.
  2235.     ErrExit: call WriteMessage
  2236.                db   0B8h     ;code for MOV AX,Immed16
  2237.     ReturnCode db   ?,4Ch
  2238.              int 21h     ;exit from program
  2239.  
  2240. The label ErrExit can be reached by JMP's from several points in the program. 
  2241. Before jumping, the code pokes in a suitable value of ReturnCode, depending on 
  2242. the type of error condition encountered. The above example uses part of the 
  2243. instruction MOV AX,4Cxxh as a variable, saving bytes.
  2244.  
  2245.               mov ax,252Fh        ;get INT 2Fh vector as ES:BX
  2246.               int 21h
  2247.               mov OldInt2F,bx     ;this example assumes CS=DS at this point
  2248.               mov OldInt2F[2],es
  2249.               mov dx,offset OurInt2F
  2250.               mov ax,252Fh        ;set INT 2Fh vector to DS:DX
  2251.               ...
  2252.     OurInt2F: cmp ax,1211h    ;a function that we want to control
  2253.               jne short JmpOldInt2F
  2254.               ... (handle this function)
  2255.               iret
  2256.     JmpOldInt2F: db     0eah    ;opcode for jump to immediate far address
  2257.     OldInt2F     dw     ?,?
  2258. This manoeuvre saves bytes versus JMP DWORD PTR OldInt2F; again, the method is 
  2259. by putting the variable (OldInt2F) right inside the code. Device drivers and 
  2260. other TSR's should use this trick, but I don't know of a single one which does 
  2261. (except my own, naturally).
  2262.  
  2263. Safe use of self-modifying code requires some awareness of on-chip instruction 
  2264. caches. It's no good to modify code in memory if what will get executed is 
  2265. already on the CPU. The following trick, however, is quite safe. Instead of:
  2266.     ErrExit2: mov al,2
  2267.               jmp short ErrExit
  2268.     ErrExit3: mov al,3
  2269.               jmp short ErrExit
  2270.     ErrExit5: mov al,5
  2271.     ErrExit: mov ah,4Ch
  2272.              int 21h
  2273.  
  2274. write:
  2275.     ErrExit2: mov al,2
  2276.               db 3Dh      ;opcode for CMP AX,immed, to disable the following                           
  2277.     ErrExit3: mov al,3    ;2-byte instruction
  2278.               db 3Dh
  2279.     ErrExit5: mov al,5
  2280.     ErrExit:  mov ah,4Ch
  2281.               int 21h
  2282.  
  2283.  
  2284. 8. Miscellaneous byte-savers.
  2285. ----------------------------
  2286. Since the instruction sets of the x86 CPU's are so elaborate, there are many 
  2287. more ad hoc ways to reduce, reuse, and recycle bytes. The following are only a 
  2288. few.
  2289.  
  2290. 8.1 After a loop, CX is 0. Thus
  2291.             mov cx,1234h
  2292.     MyLoop: ...
  2293.             ...
  2294.             loop MyLoop
  2295.             mov cx,56h
  2296.             ...
  2297. is wasteful. The last instruction should be
  2298.     mov cl,56h.
  2299.  
  2300. 8.2 Use conditional MOV's.
  2301.  
  2302.                     cmp VideoMode,7
  2303.                     je BlackAndWhite
  2304.                     mov dx,0B800h
  2305.                     jmp short Either
  2306.     BlackAndWhite:  mov dx, 0B000h
  2307.     Either:
  2308.     ...
  2309. The above code wastes bytes. Better is:
  2310.                 mov dx, 0B800h
  2311.                 cmp VideoMode,7
  2312.                 jne GotVideoBase
  2313.                 mov dh,0B0h
  2314.     GotVideoBase:
  2315.                 ...
  2316. The improved version has one jump instruction instead of two, and in this 
  2317. example saves an additional byte by resetting only DH, not DX.
  2318.  
  2319. With the Pentium, Intel introduced a useful set of conditional mov's right into 
  2320. the instruction set.
  2321.  
  2322. 8.3 To test the high bit of a register, avoid the constants 80h and 8000h.
  2323. For example,
  2324.     test dh,80h
  2325.     jnz MyLabel
  2326. is 5 bytes, but
  2327.     or dh,dh
  2328.     js MyLabel
  2329. is 4. The latter instruction also leaves more information in the flags. 
  2330. TEST DH,DH or AND DH,DH have the same effect as OR DH,DH.
  2331.  
  2332. 8.4 To determine if several variables of the same size are all 0, OR them 
  2333. together, and the zero flag will tell you. To determine if they are all -1, 
  2334. AND them together and increment the result.
  2335.  
  2336.  
  2337. 9. Postlude
  2338. -----------
  2339. Intel makes their excellent CPU documentation available free, from:
  2340.     http://developer.intel.com/design/litcentr/index.htm
  2341. It is in Adobe PDF format; you will need the Acrobat Reader, also free, from:
  2342.     http://www.adobe.com/prodindex/acrobat/readstep.html
  2343.  
  2344. If all else fails, you can try to wake me up at:
  2345.     hammick@bc.sympatico.ca
  2346. Regards from Vancouver,
  2347. Larry
  2348.  
  2349.  
  2350. ::/ \::::::.
  2351. :/___\:::::::.
  2352. /|    \::::::::.
  2353. :|   _/\:::::::::.
  2354. :| _|\  \::::::::::.
  2355. :::\_____\:::::::::::................................WIN32.ASSEMBLY.PROGRAMMING
  2356.                                                      A Simple Window
  2357.                                                      by Iczelion
  2358.  
  2359.  
  2360. In this tutorial, we will build a Windows program that displays a fully 
  2361. functional window on the desktop.
  2362.  
  2363.  
  2364. Download the example file here.
  2365. http://203.148.211.201/iczelion/files/tut03.zip
  2366.  
  2367. Preliminary:
  2368.  
  2369. Windows programs rely heavily on API functions for their GUI. This approach 
  2370. benefits both users and programmers. For users, they don't have to learn how to 
  2371. navigate the GUI of each new programs, the GUI of Windows programs are alike. 
  2372. For programmers, the GUI codes are already there,tested, and ready for use. The 
  2373. downside for programmers is the increased complexity involved. In order to 
  2374. create or manipulate any GUI objects such as windows, menu or icons, 
  2375. programmers must follow a strict recipe. But that can be overcome by modular
  2376. programming or OOP paradigm.
  2377.  
  2378. I'll outline the steps required to create a window on the desktop below:
  2379.  
  2380.   1.Get the instance handle of your program (required)
  2381.   2.Get the command line (not required unless your program receives command 
  2382.     line)
  2383.   3.Register window class (required ,unless you use predefined window types, eg.
  2384.      MessageBox)
  2385.   4.Create the window (required)
  2386.   5.Show the window on the desktop (required unless you don't want to show the 
  2387.     window immediately)
  2388.   6.Refresh the client area of the window
  2389.   7.Enter an infinite loop, checking for message from Windows
  2390.   8.If messages arrive, they are processed by a specialized function that is 
  2391.     responsible for the window
  2392.   9.Quit program if the user closes the window
  2393.  
  2394. As you can see, the structure of a Windows program is rather complex compared 
  2395. to a DOS program. But the world of Windows is drastically different from the 
  2396. world of DOS. Windows programs must be able to coexist peacefully with each 
  2397. other. They must follow stricter rules. You, as a programmer, must also be more 
  2398. strict with your programming style and habit.
  2399.  
  2400. Content:
  2401.  
  2402. Below is the source code of our simple window program. Before jumping into the 
  2403. gory details of Win32 ASM programming, I'll point out some fine points which'll 
  2404. ease your programming.
  2405.  
  2406. You should put all Windows constants, structures and function prototypes in an 
  2407. include file and include it at the beginning of your .asm file. It'll save you 
  2408. a lot of effort and avoid typing errors. Most of the time, you can use  include 
  2409. file from some Win32 asm examples. I have used windows.inc from Steve Gibson's 
  2410. Small Is Beautiful exampleand made some additions of my own.
  2411.  
  2412. Use IncludeLib directive to specify the import library used in your program. 
  2413. For example, if your program calls MessageBoxA, you should put the line:
  2414.                IncludeLib user32.lib
  2415. at the beginning of your .asm file. This directive tells MASM that your program 
  2416. will make usesof functions in that import library. If your program calls 
  2417. functions in more than one library, just add an includelib for each library you 
  2418. use. Using IncludeLib directive, you don't have to worry about import libraries 
  2419. at link time. You can use the /LIBPATH linker switch to tell Link where all the 
  2420. libs are. 
  2421.  
  2422. When declaring  API function prototypes, structures, or constants in 
  2423. your include file, try to stick to the original names used in Windows include 
  2424. files, including case. This will save you a lot of headache when looking up 
  2425. some item in Win32 API reference. 
  2426.  
  2427. Use makefile to automate your assembling process. This will save you a lot of 
  2428. typing.
  2429.  
  2430. ; =============================================================================
  2431. include windows.inc                       ; .386 and .model are already
  2432. declared in windows.inc
  2433. includelib user32.lib                     ; calls to functions in
  2434. user32.lib and kernel32.lib
  2435. includelib kernel32.lib
  2436.  
  2437. .DATA                                     ; initialized data
  2438.     ClassName db "SimpleWinClass",0       ; the name of our window class
  2439.     AppName  db "Our First Window",0      ; the name of our window
  2440.  
  2441. .DATA?                                    ; Uninitialized data
  2442. hInstance HINSTANCE ?                     ; Instance handle of our program
  2443. CommandLine LPSTR ?
  2444.  
  2445. .CODE                                     ; Here begins our code
  2446.  start:
  2447.      invoke GetModuleHandle, NULL         ; get the instance handle of our program.
  2448.                                           ; Under Win32, hmodule==hinstance
  2449.      mov    hInstance,eax
  2450.      invoke GetCommandLine                ; get the command line. 
  2451.      mov    CommandLine,eax
  2452.      invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT  ; call  Winmain
  2453.      invoke ExitProcess,eax               ; quit our program. The exit code is
  2454.                                           ; returned in eax from WinMain.
  2455.  
  2456. WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
  2457.     LOCAL wc:WNDCLASSEX                    ; create local variables on stack
  2458.     LOCAL msg:MSG
  2459.     LOCAL hwnd:HWND
  2460.  
  2461.     mov   wc.cbSize,SIZEOF WNDCLASSEX      ; fill values in members of wc
  2462.     mov   wc.style, CS_HREDRAW or CS_VREDRAW
  2463.     mov   wc.lpfnWndProc, OFFSET WndProc
  2464.     mov   wc.cbClsExtra,NULL
  2465.     mov   wc.cbWndExtra,NULL
  2466.     push  hInstance
  2467.     pop   wc.hInstance
  2468.     mov   wc.hbrBackground,COLOR_WINDOW+1
  2469.     mov   wc.lpszMenuName,NULL
  2470.     mov   wc.lpszClassName,OFFSET ClassName
  2471.     invoke LoadIcon,NULL,IDI_APPLICATION
  2472.     mov   wc.hIcon,eax
  2473.     mov   wc.hIconSm,0
  2474.     invoke LoadCursor,NULL,IDC_ARROW
  2475.     mov   wc.hCursor,eax
  2476.     invoke RegisterClassEx, addr wc       ; register our window class
  2477.  
  2478.     invoke CreateWindowEx,NULL,\
  2479.                 ADDR ClassName,\
  2480.                 ADDR AppName,\
  2481.                 WS_OVERLAPPEDWINDOW,\
  2482.                 CW_USEDEFAULT,\
  2483.                 CW_USEDEFAULT,\
  2484.                 CW_USEDEFAULT,\
  2485.                 CW_USEDEFAULT,\
  2486.                 NULL,\
  2487.                 NULL,\
  2488.                 hInst,\
  2489.                 NULL
  2490.     mov   hwnd,eax
  2491.     invoke ShowWindow, hwnd,CmdShow       ; display our window on desktop
  2492.     invoke UpdateWindow, hwnd             ; refresh the client area
  2493.  
  2494.     .WHILE TRUE                           ; Enter message loop
  2495.                 invoke GetMessage, ADDR msg,NULL,0,0
  2496.                 .BREAK .IF (!eax)
  2497.                 invoke TranslateMessage, ADDR msg
  2498.                 invoke DispatchMessage, ADDR msg
  2499.    .ENDW
  2500.     mov     eax,msg.wParam                ; return exit code in  eax
  2501.     ret
  2502. WinMain endp
  2503.  
  2504. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  2505.     mov   eax,uMsg                       ; put the window message in eax 
  2506.     .IF eax==WM_DESTROY                  ; if the user closes our window
  2507.         invoke PostQuitMessage,NULL      ; quit our application
  2508.         xor eax,eax
  2509.     .ELSE                                ; Default message processing
  2510.        invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
  2511.     .ENDIF
  2512.  ret
  2513. WndProc endp
  2514.  
  2515. end start
  2516.  
  2517. You may be taken aback that a simple Windows program requires so much coding. 
  2518. But most of these codes are just *template* codes that you can copy from one 
  2519. source code to another. Or, if you prefer, you could assemble some of these 
  2520. codes into a library to be used as prologue and epilogue codes. You can write 
  2521. only the codes in WinMain function. In fact, this is what C compilers do. They 
  2522. let you write WinMain codes without worrying about other housekeeping chores. 
  2523. The only catch is that you must have a function named WinMain else C compilers 
  2524. will not be able to combine your codes with the prologue and epilogue. You do 
  2525. not have such restriction with assembly language. You can use any function name 
  2526. instead of WinMain or no function at all.
  2527.  
  2528. Prepare yourself. This is going to be a long, long tutorial. Let's analyze this 
  2529. program to death!
  2530.  
  2531.      include windows.inc
  2532.      includelib user32.lib
  2533.      includelib kernel32.lib
  2534.  
  2535. We must include windows.inc at the beginning of the source code. It contains 
  2536. important API function prototypes, structures and constants that are used by 
  2537. our program. The include file , windows.inc, is just a text file. You can open 
  2538. it with any text editor. The first two lines are .386 and .model directives, so 
  2539. you don't have to specify these two lines at the beginning of the source code. 
  2540.  
  2541. Next are several macros that its author (Steve Gibson) frequently uses. The 
  2542. remaining of the file contains important structures, constants and function 
  2543. prototypes. Please note that windows.inc does not contain all structures,
  2544. constants, and function prototypes of Windows. It just holds the most 
  2545. frequently used ones. You can add in new items if they are not in the file.
  2546.  
  2547. Our program calls API functions that reside in user32.dll (CreateWindowEx,
  2548. RegisterWindowClassEx, for example) and kernel32.dll (ExitProcess), so we must 
  2549. link our program to those two import libraries. The next question : how can I 
  2550. know which import library should be linked to my program? The answer: You must 
  2551. know where the API functions called by your program reside. For example, if you 
  2552. call an API function in gdi32.dll, you must link with gdi32.lib.
  2553.  
  2554. This is the approach of MASM. TASM 's way of import library linking is much 
  2555. more simpler: just link to one and only one file: import32.lib.
  2556.  
  2557.      .DATA
  2558.          ClassName db "SimpleWinClass",0
  2559.          AppName  db "Our First Window",0
  2560.  
  2561.      .DATA?
  2562.      hInstance HINSTANCE ?
  2563.      CommandLine LPSTR ?
  2564.  
  2565. Next are the "DATA" sections.
  2566. In .DATA, we declare two zero-terminated strings(ASCIIZ strings): ClassName 
  2567. which is the name of our window class and AppName which is the name of our 
  2568. window. Note that the two variables are initialized.
  2569.  
  2570. In .DATA?, three variables are declared: hInstance (instance handle of our 
  2571. program), CommandLine (command line of our program), and CommandShow (state of 
  2572. our window on first appearance). The unfamiliar data types, HINSTANCE and LPSTR, 
  2573. are really new names for DWORD. You can look them up in windows.inc. Note that 
  2574. all variables in .DATA? section are not initialized, that is, they don't have 
  2575. to hold any specific value on startup, but we want to reserve the space for 
  2576. future use.
  2577.  
  2578.      .CODE
  2579.       start:
  2580.           invoke GetModuleHandle, NULL
  2581.           mov    hInstance,eax
  2582.           invoke GetCommandLine
  2583.           mov    CommandLine,eax
  2584.           invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
  2585.           invoke ExitProcess,eax
  2586.           .....
  2587.      end start
  2588.  
  2589. .CODE contains all your instructions. Your codes must reside between <starting 
  2590. label>: and end <starting label>. The name of the label is unimportant. You can 
  2591. name it anything you like so long as it doesn't violate the naming convention 
  2592. of MASM.
  2593.  
  2594. Our first instruction is the call to GetModuleHandle to retrieve the instance 
  2595. handle of our program. Under Win32, instance handle and module handle are one 
  2596. and the same. You can think of instance handle as the ID of your program. It is 
  2597. used as parameter to several API functions our program must call, so it's 
  2598. generally a good idea to retrieve it at the beginning of our program.
  2599.  
  2600. Upon return from a Win32 function, the function return value, if any, can be 
  2601. found in eax. All other values are returned through variables passed in the 
  2602. function parameter list you defined for the call.
  2603.  
  2604. A Win32 function that you call will always preserve the segment registers and 
  2605. the ebx, edi, esi and ebp registers. Conversely, ecx and edx are considered 
  2606. scratch registers and are always undefined upon return from a Win32 function.
  2607. The bottom line is that: when calling an API function, expect return value in 
  2608. eax. If any of your function will be called by Windows, you must also play by 
  2609. the rule: preserve and restore the values of the segment registers, ebx, edi, 
  2610. esi and ebp upon function return else your program will crash very shortly.
  2611.  
  2612. The GetCommandLine call is unnecessary if your program doesn't process a 
  2613. command line. In this example, I show you how to call it in case you need it in 
  2614. your program.
  2615.  
  2616. Next is the WinMain() call. Here it receives four parameters: the instance 
  2617. handle of our program, the instance handle of the previous instance of our 
  2618. program, the command line and window state at first appearance. Under Win32, 
  2619. there's NO previous instance. Each program is alone in its address space, so 
  2620. the value of hPrevInst is always 0. This is a lefover from the day of Win16.
  2621.  
  2622. Note: You don't have to declare the function name as WinMain. In fact, you have 
  2623. complete freedom in this regard. You don't have to use any WinMain-equivalent 
  2624. function at all. You can paste the codes in WinMain next to GetCommandLine and 
  2625. your program will still be able to function perfectly. 
  2626.  
  2627. Upon return from WinMain, eax is filled with exit code. We pass that exit code 
  2628. as parameter to ExitProcess which terminates our application.
  2629.  
  2630. WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
  2631.  
  2632. The above line is the function declaration of WinMain. Note the parameter:type 
  2633. pairs that follow PROC directive. They are parameters that WinMain receives 
  2634. from the caller. You can refer to these parameters by name instead of by stack 
  2635. manipulation. In addition, MASM will generate the prologue and epilogue codes 
  2636. for the function. So we don't have to concern ourselves with stack frame on 
  2637. function enter and exit.
  2638.  
  2639.     LOCAL wc:WNDCLASSEX
  2640.     LOCAL msg:MSG
  2641.     LOCAL hwnd:HWND
  2642.  
  2643. LOCAL directive allocates memory from the stack for local variables used in the 
  2644. function. The LOCAL directive is immediately followed by <the name of local 
  2645. variable>:<variable type>.
  2646.  
  2647. So LOCAL wc:WNDCLASSEX tells MASM to allocate memory from the stack the size of 
  2648. WNDCLASSEX structure for the variable named wc. We can refer to wc in our codes 
  2649. without any difficulty involved in stack manipulation. That's really a godsend, 
  2650. I think. The downside  is that local variables cannot be used outside the 
  2651. function they're created and will be automatically destroyed when the function 
  2652. returns to the caller. Another drawback is that you cannot initialize local 
  2653. variables automatically because they're just stack memory allocated dynamically 
  2654. on function start. You have to manually assign them with desired values after 
  2655. LOCAL directives.
  2656.  
  2657.     mov   wc.cbSize,SIZEOF WNDCLASSEX
  2658.     mov   wc.style, CS_HREDRAW or CS_VREDRAW
  2659.     mov   wc.lpfnWndProc, OFFSET WndProc
  2660.     mov   wc.cbClsExtra,NULL
  2661.     mov   wc.cbWndExtra,NULL
  2662.     push  hInstance
  2663.     pop   wc.hInstance
  2664.     mov   wc.hbrBackground,COLOR_WINDOW+1
  2665.     mov   wc.lpszMenuName,NULL
  2666.     mov   wc.lpszClassName,OFFSET ClassName
  2667.     invoke LoadIcon,NULL,IDI_APPLICATION
  2668.     mov   wc.hIcon,eax
  2669.     mov   wc.hIconSm,0
  2670.     invoke LoadCursor,NULL,IDC_ARROW
  2671.     mov   wc.hCursor,eax
  2672.     invoke RegisterClassEx, addr wc        
  2673.                 ; register our window class
  2674.  
  2675. The inimidating lines above are really simple in concept. It just takes several 
  2676. lines of instruction to accomplish. The concept behind all these lines is 
  2677. window class. A window class is nothing more than a blueprint or specification 
  2678. of a window. It defines several important characteristics of a window such as 
  2679. its icon, its cursor, the function responsible for it, its color etc. You 
  2680. create a window from a window class. This is some sort of object oriented 
  2681. concept. If you want to create more than one window with the same character-
  2682. istics, it stands to reason to store all these characteristics in only one 
  2683. place and refer to them when needed. This scheme will save lots of memory by 
  2684. avoiding duplication of information.
  2685.  
  2686. Remember, Windows is designed in the past when memory chips are prohibitive and
  2687. most computers have 1 MB of memory. Windows must be very efficient in using the 
  2688. scarce memory resource. The point is: if you define your own window, you must 
  2689. fill the desired characteristics of your window in a WNDCLASS or WNDCLASSEX 
  2690. structure and call RegisterClass or RegisterClassEx before you're able to 
  2691. create your window. You only have to register the window class once for each 
  2692. window type you want to create a window from.
  2693.  
  2694. Windows have several predefined Window classes, such as button and edit box. 
  2695. For these windows (or controls), you don't have to register a window class, 
  2696. just call CreateWindowEx with the predefined class name.
  2697.  
  2698. The single most important member in the WNDCLASSEX is lpfnWndProc. lpfn stands 
  2699. for long pointer to function. Under Win32, there's no "near" or "far" pointer, 
  2700. just pointer because of the new FLAT memory model. But this is again a lefover 
  2701. from the day of Win16. Each window class must be associated with a function 
  2702. called window procedure. The window procedure is responsible for message 
  2703. handling of all windows created from the associated window class.
  2704.  
  2705. Windows will send messages to the window procedure to notify it of important 
  2706. events concerning the windows it 's responsible for,such as user keyboard or 
  2707. mouse input. It's up to the window procedure to respond intelligently to each 
  2708. window message it receives. You will spend most of your time writing event 
  2709. handlers in window procedure.
  2710.  
  2711. I'll describe each member of WNDCLASSEX below:
  2712.  
  2713. typedef struct tagWNDCLASSEX {
  2714.     UINT                cbSize;
  2715.     UINT                style;
  2716.     WNDPROC     lpfnWndProc;
  2717.     int                     cbClsExtra;
  2718.     int                     cbWndExtra;
  2719.     HINSTANCE   hInstance;
  2720.     HICON             hIcon;
  2721.     HCURSOR       hCursor;
  2722.     HBRUSH          hbrBackground;
  2723.     LPCSTR           lpszMenuName;
  2724.     LPCSTR           lpszClassName;
  2725.     HICON             hIconSm;
  2726. } WNDCLASSEX;
  2727.  
  2728. cbSize: The size of WNDCLASSEX structure in bytes. We can use SIZEOF operator 
  2729. to get the value.
  2730.  
  2731. style: The style of windows created from this class. You can combine several 
  2732. styles together using "or" operator.
  2733.  
  2734. lpfnWndProc: The address of the window procedure responsible for windows 
  2735. created from this class.
  2736.  
  2737. cbClsExtra: Specifies the number of extra bytes to allocate following the 
  2738. window-classstructure. The operating system initializes the bytes to zero.
  2739.  
  2740. cbWndExtra: Specifies the number of extra bytes to allocate following the window 
  2741. instance. The operating system initializes the bytes to zero. If an application 
  2742. uses the WNDCLASS structure to register a dialog box created by using the CLASS 
  2743. directive in the resource file, it must set this member to DLGWINDOWEXTRA.
  2744.  
  2745. hInstance: Instance handle of the module.
  2746.  
  2747. hIcon: Handle to the icon. Get it from LoadIcon call.
  2748.  
  2749. hCursor: Handle to the cursor. Get it from LoadCursor call.
  2750.  
  2751. hbrBackground: Background color of windows created from the class.
  2752.  
  2753. lpszMenuName: Default menu handle for windows created from the class.
  2754.  
  2755. lpszClassName: The name of this window class.
  2756.  
  2757. hIconSm: Handle to a small icon that is associated with the window class. If 
  2758. this member is NULL, the system searches the icon resource specified by the 
  2759. hIcon member for an icon of the appropriate size to use as the small icon.
  2760.     invoke CreateWindowEx, NULL,\
  2761.                                                 ADDR ClassName,\
  2762.                                                 ADDR AppName,\
  2763.                                                 WS_OVERLAPPEDWINDOW,\
  2764.                                                 CW_USEDEFAULT,\
  2765.                                                 CW_USEDEFAULT,\
  2766.                                                 CW_USEDEFAULT,\
  2767.                                                 CW_USEDEFAULT,\
  2768.                                                 NULL,\
  2769.                                                 NULL,\
  2770.                                                 hInst,\
  2771.                                                 NULL
  2772.  
  2773. After registering the window class, we can call CreateWindowEx to create our 
  2774. window based on the submitted window class. Notice that there're 12 parameters 
  2775. to this function. C function prototype of CreateWindowEx is below:
  2776. HWND
  2777. WINAPI
  2778. CreateWindowExA(
  2779.     DWORD dwExStyle,
  2780.     LPCSTR lpClassName,
  2781.     LPCSTR lpWindowName,
  2782.     DWORD dwStyle,
  2783.     int X,
  2784.     int Y,
  2785.     int nWidth,
  2786.     int nHeight,
  2787.     HWND hWndParent ,
  2788.     HMENU hMenu,
  2789.     HINSTANCE hInstance,
  2790.     LPVOID lpParam);
  2791.  
  2792. Let's see detailed description of each parameter:
  2793. dwExStyle: Extra window styles. This is the new parameter that is added to the 
  2794. old CreateWindow. You can put new window styles for Windows 95 & NT here. You 
  2795. can specify your ordinary window style in dwStyle but if you want some special 
  2796. styles such as topmost window, you must specify them here. You can use NULL if 
  2797. you don't want extra window styles.
  2798.  
  2799. lpClassName: (Required). Address of the ASCIIZ string containing the name of 
  2800. window class you want to use as template for this window. The Class can be your 
  2801. own registered class or predefined window class. As stated above, every window 
  2802. you created must be based on a window class.
  2803.  
  2804. lpWindowName: Address of the ASCIIZ string containing the name of the window. 
  2805. It'll be shown on the title bar of the window. If this parameter is NULL, the 
  2806. title bar of the window will be blank.
  2807.  
  2808. dwStyle:  Styles of the window. You can specify the appearance of the window 
  2809. here. Passing NULL  is ok but the window will have no system menu box, no 
  2810. minimize-maximize buttons, and no close-window button. The window would not be 
  2811. of much use at all. You will need to press Alt+F4 to close it. The most common 
  2812. window style is WS_OVERLAPPEDWINDOW. A window style is only a bit flag. Thus 
  2813. you can combine several window styles by "or" operator to achieve the desired 
  2814. appearance of the window. WS_OVERLAPPEDWINDOW style is actually a combination 
  2815. of the most common window styles by this method.
  2816.  
  2817. X,Y: The coordinate of the upper left corner of the window. Normally this 
  2818. values should be CW_USEDEFAULT, that is, you want Windows to decide for you 
  2819. where to put the window on the desktop.
  2820.  
  2821. nWidth, nHeight: The width and height of the window in pixels. You can also use
  2822. CW_USEDEFAULT to let Windows choose the appropriate width and height for you.
  2823.  
  2824. hWndParent: A handle to the window's parent window (if exists). This parameter 
  2825. tells Windows whether this window is a child (subordinate) of some other window 
  2826. and, if it is, which window is the parent. Note that this is not the parent-
  2827. child relationship of multiple document interface (MDI). Child windows are not 
  2828. bound to the client area of the parent window. This relationship is 
  2829. specifically for Windows internal use. If the parent window is destroyed, all 
  2830. child windows will be destroyed automatically. It's really that simple. Since
  2831. in our example, there's only one window, we specify this parameter as NULL.
  2832.  
  2833. hMenu: A handle to the window's menu. NULL if the class menu is to be used. 
  2834. Look back at the a member of WNDCLASSEX structure, lpszMenuName. lpszMenuName 
  2835. specifies *default* menu for the window class. Every window created from this 
  2836. window class will have the same menu by default. Unless you specify an 
  2837. *overriding* menu for a specific window via its hMenu parameter. hMenu is 
  2838. actually a dual-purpose parameter. In case the window you want to create
  2839. is of a predefined window type (ie. control), such control cannot own a menu. 
  2840. hMenu is used as that control's ID instead. Windows can decide whether hMenu is 
  2841. really a menu handle or a control ID by looking at lpClassName parameter. If 
  2842. it's the name of a predefined window class, hMenu is a control ID. If it's not, 
  2843. then it's a handle to the window's menu.
  2844.  
  2845. hInstance: The instance handle for the program module creating the window.
  2846.  
  2847. lpParam: Optional pointer to a data structure passed to the window. This is 
  2848. used by MDI window to pass the CLIENTCREATESTRUCT data. Normally, this value is 
  2849. set to NULL, meaning that no data is passed via CreateWindow(). The window can 
  2850. retrieve the value of this parameter by the call to GetWindowLong function.
  2851.  
  2852.     mov   hwnd,eax
  2853.     invoke ShowWindow, hwnd,CmdShow
  2854.     invoke UpdateWindow, hwnd
  2855.  
  2856. After successful return from CreateWindowEx, the window handle is stored in eax. 
  2857. We must keep this value for future use. The window we just created is not 
  2858. automatically displayed. You must call ShowWindow with the window handle and 
  2859. the desired *display state* of the window to make it display on the screen. 
  2860. Next you can call UpdateWindow to order your window to repaint its client area. 
  2861. This function is useful when you want to update the content of the client area. 
  2862. You can omit this call though.
  2863.  
  2864.     .WHILE TRUE
  2865.                 invoke GetMessage, ADDR msg,NULL,0,0
  2866.                 .BREAK .IF (!eax)
  2867.                 invoke TranslateMessage, ADDR msg
  2868.                 invoke DispatchMessage, ADDR msg
  2869.    .ENDW
  2870.  
  2871. At this time, our window is up on the screen. But it cannot receive input from 
  2872. the world. So we have to *inform* it of relevant events. We accomplish this 
  2873. with a message loop. There's only one message loop for each module. This 
  2874. message loop continually checks for messages from Windows with GetMessage call. 
  2875. GetMessage passes a pointer to a MSG structure to Windows. This MSG structure 
  2876. will be filled with information about the message that Windows want to send to 
  2877. a window in the module. GetMessage function will not return until there's a
  2878. message for a window in the module. During that time, Windows can give control 
  2879. to other programs. This is what forms the cooperative multitasking scheme of 
  2880. Win16 platform. GetMessage returns FALSE if WM_QUIT message is received which, 
  2881. in the message loop, will terminate the loop and exit the program.
  2882.  
  2883. TranslateMessage is a utility function that takes raw keyboard input and 
  2884. generates a new message (WM_CHAR) that is placed on the message queue. The 
  2885. message with WM_CHAR contains the ASCII value for the key pressed, which is 
  2886. easier to deal with than the raw keyboard scan codes. You can omit this call if 
  2887. your program doesn't process keystrokes. DispatchMessage sends the message data 
  2888. to the window procedure responsible for the specific window the message is for.
  2889.  
  2890.     mov     eax,msg.wParam
  2891.     ret
  2892. WinMain endp
  2893.  
  2894. If the message loop terminates, the exit code is stored in wParam member of the 
  2895. MSG structure. You can store this exit code into eax to return it to Windows. 
  2896. At the present time, Windows do not make use of the return value, but it's 
  2897. better to be on the safe side and plays by the rule.
  2898.  
  2899. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  2900.  
  2901. This is our window procedure. You don't have to name it WndProc. The first 
  2902. parameter, hWnd, is the window handle of the window that the message is destined. 
  2903. uMsg is the message. Note that uMsg is not a MSG structure. It's just a number, 
  2904. really. Windows define hundreds of messages, most of which your programs will 
  2905. not be interested in. Windows will send an appropriate message to a window in 
  2906. case something relevant to that window happens. Thew indow procedure receives 
  2907. the message and react to it intelligently. wParam and lParam are just extra 
  2908. parameters for use by some message. Some message does send accompanying data in
  2909. addition to the message itself. Those data are passed to the window procedure 
  2910. by means of lParam and wParam.
  2911.  
  2912. mov   eax,uMsg
  2913.     .IF eax==WM_DESTROY
  2914.         invoke PostQuitMessage,NULL
  2915.         xor eax,eax
  2916.     .ELSE
  2917.         invoke DefWindowProc,hWnd,uMsg,wParam,lParam
  2918.     .ENDIF
  2919.  ret
  2920. WndProc endp
  2921.  
  2922. Here comes the crucial part. This is where most of your program's intelligence
  2923. resides. The code that responds to each Windows message are in the window 
  2924. procedure. Your code must check  the Windows message to see if it's a message 
  2925. it's interested in. If it is, do anything you want to do in response to that 
  2926. message and then return with zero in eax. If it's not, you MUST pass ALL 
  2927. parameters for default processing by DefWindowProc. This DefWindowProc is an
  2928. API function that processes the messages your program is not interested in.
  2929.  
  2930. The only message that you MUST respond to is WM_DESTROY. This message is sent 
  2931. to your window procedure whenever your window is closed. At the time your 
  2932. window procedure receives this message, your window is removed from the screen. 
  2933. This is just a notification that your window is now destroyed, you should 
  2934. prepare yourself to return to Windows. In response to this, you can perform 
  2935. housekeeping prior to return to Windows. You have no choice but to quit when it 
  2936. comes to this state. If you want to have a chance to stop the user from closing
  2937. your window, you should process WM_CLOSE message. Now back to WM_DESTROY, after 
  2938. performing  housekeeping chores, you must call PostQuitMessage which will post 
  2939. WM_QUIT back to your module. WM_QUIT will make GetMessage return with zero 
  2940. value in eax, which in turn, terminates the message loop and quits to Windows. 
  2941. You can send WM_DESTROY message to your own window procedure by calling 
  2942. DestroyWindow function.
  2943.  
  2944.       [Reprinted With permission from Iczelion's Win32 Assembly HomePage]
  2945.                    http://203.148.211.201/iczelion/index.html
  2946.  
  2947.  
  2948. ::/ \::::::.
  2949. :/___\:::::::.
  2950. /|    \::::::::.
  2951. :|   _/\:::::::::.
  2952. :| _|\  \::::::::::.
  2953. :::\_____\:::::::::::................................WIN32.ASSEMBLY.PROGRAMMING
  2954.                                                      Painting with Text
  2955.                                                      by Iczelion
  2956.  
  2957.  
  2958. In this tutorial, we will learn how to "paint" text in the client area of a 
  2959. window. We'll also learn about device context.
  2960.  
  2961. You can download the source code here.
  2962. http://203.148.211.201/iczelion/files/tut04.zip
  2963.  
  2964. Preliminary
  2965. -----------
  2966. Text in Windows is a type of GUI object.  Each character is composed of 
  2967. numerous pixels that are lumped together into a distinct pattern. That's why 
  2968. it's called "painting" instead of "writing". Normally, you paint text in your 
  2969. own client area (actually, you can paint outside client area but that's another 
  2970. story).  
  2971.  
  2972. Putting text on screen in Windows is drastically different from DOS. In DOS, 
  2973. you can think of the screen in 80x25 dimension. But in Windows, the screen are 
  2974. shared by several programs. Some rules must be enforced to avoid programs 
  2975. writing over each other screen data. Windows ensures this by limiting painting 
  2976. area of each window to its own client area only. The size of client area of a 
  2977. window is not constant. The user can change the size anytime. So you must 
  2978. determine the dimension of client area dynamically, at runtime.
  2979.  
  2980. Before you can paint something on the client area, you must ask for permission 
  2981. from Windows. That's right, you don't have absolute control of the screen as 
  2982. you were in DOS.  You must ask Windows for permission to paint your own client 
  2983. area. Windows will determine the size of your client area, font, colors and 
  2984. other GDI attributes and send a handle to device context back to your program. 
  2985. You can then use the device context as a passport to painting on your client 
  2986. area.
  2987.  
  2988. What is a device context? It's just a data structure maintained internally by 
  2989. Windows. A device context is associated with a particular device, such as a 
  2990. printer or video display. For a video display, a device context is usually 
  2991. associated with a particular window on the display.
  2992.  
  2993. Some of the values in the device context are graphic attributes such as colors, 
  2994. font etc. These are default values which you can change at will. They exist to 
  2995. help reduce the load from having to specify these attributes in every GDI 
  2996. function calls.
  2997.  
  2998. When a program need to paint, it must obtain a handle to a device context. 
  2999. Normally, there's two ways to accomplish this.
  3000.      call BeginPaint in response to WM_PAINT message.
  3001.      call GetDC in response to other messages.
  3002.  
  3003. One thing you must remember, after you're through with the device context 
  3004. handle, you must release it during the processing of a single message. Don't 
  3005. obtain the handle in response to one message and release it in response to 
  3006. another.
  3007.  
  3008. Windows posts WM_PAINT messages to a window to notify that it's now time to 
  3009. repaint its client area. Windows does not save the content of client area of a 
  3010. window.  Instead, when a situation occurs that warrants a repaint of client 
  3011. area (such as when a window was covered by another and is just brought back in 
  3012. front), Windows put WM_PAINT message in that window's message queue. It's the 
  3013. responsibility of that window to repaint its own client area. You must gather 
  3014. all information about how to repaint your client area in the WM_PAINT section 
  3015. of your window procedure, so the window procudure can repaint the client area 
  3016. when WM_PAINT message arrives.
  3017.  
  3018. Another concept you must come to terms with is the invalid rectangle. Windows 
  3019. defines an invalid rectangle as the smallest rectangular area in the client 
  3020. area that needs to be repainted. When Windows detects an invalid rectangle in
  3021. the client area of a window , it posts WM_PAINT message to that window. In 
  3022. response to WM_PAINT message, the window can obtain a paintstruct structure 
  3023. which contains, among others, the coordinate of the invalid rectangle. 
  3024. You call BeginPaint in response to WM_PAINT message to validate the invalid
  3025. rectangle. If you don't process WM_PAINT message, at the very least you must 
  3026. call DefWindowProc or ValidateRect to validate the invalid rectangle else 
  3027. Windows will repeatedly send you WM_PAINT message.
  3028.  
  3029. Here's the steps you perform in response to a WM_PAINT message:
  3030.  
  3031.      Get a handle to device context with BeginPaint.
  3032.      Paint the client area.
  3033.      Release the handle to device context with EndPaint
  3034.  
  3035. Note that you don't have to explicitly validate the invalid rectangle. It's 
  3036. automatically done by the BeginPaint call. Between BeginPaint-Endpaint pair, 
  3037. you can call any GDI functions to paint your client area. Nearly everyone of 
  3038. them requires a handle to device context as a parameter.
  3039.  
  3040. Content:
  3041.  
  3042. We will write a program that display a text string "Win32 assembly is great 
  3043. and easy!" in the center of the client area.
  3044.  
  3045.  
  3046.      include windows.inc
  3047.      includelib user32.lib
  3048.      includelib kernel32.lib
  3049.  
  3050.      .DATA
  3051.      ClassName db "SimpleWinClass",0
  3052.      AppName  db "Our First Window",0
  3053.      OurText  db "Win32 assembly is great and easy!",0
  3054.  
  3055.      .DATA?
  3056.      hInstance HINSTANCE ?
  3057.      CommandLine LPSTR ?
  3058.  
  3059.      .CODE
  3060.              start:
  3061.       invoke GetModuleHandle, NULL
  3062.       mov    hInstance,eax
  3063.       invoke GetCommandLine
  3064.       invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
  3065.       invoke ExitProcess,eax
  3066.  
  3067. WinMain proc hinst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:SDWORD
  3068.          LOCAL wc:WNDCLASSEX
  3069.          LOCAL msg:MSG
  3070.          LOCAL hwnd:HWND
  3071.          mov   wc.cbSize,SIZEOF WNDCLASSEX
  3072.          mov   wc.style, CS_HREDRAW or CS_VREDRAW
  3073.          mov   wc.lpfnWndProc, OFFSET WndProc
  3074.          mov   wc.cbClsExtra,NULL
  3075.          mov   wc.cbWndExtra,NULL
  3076.          push  hInstance
  3077.          pop   wc.hInstance
  3078.          mov   wc.hbrBackground,COLOR_WINDOW+1
  3079.          mov   wc.lpszMenuName,NULL
  3080.          mov   wc.lpszClassName,OFFSET ClassName
  3081.          invoke LoadIcon,NULL,IDI_APPLICATION
  3082.          mov   wc.hIcon,eax
  3083.          mov   wc.hIconSm,0
  3084.          invoke LoadCursor,NULL,IDC_ARROW
  3085.          mov   wc.hCursor,eax
  3086.          invoke RegisterClassEx, addr wc
  3087.          invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
  3088.                 WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
  3089.                 CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
  3090.                 hInst,NULL
  3091.          mov   hwnd,eax
  3092.          invoke ShowWindow, hwnd,SW_SHOWNORMAL
  3093.          invoke UpdateWindow, hwnd
  3094.              .WHILE TRUE
  3095.                      invoke GetMessage, ADDR msg,NULL,0,0
  3096.                      .BREAK .IF (!eax)
  3097.                      invoke TranslateMessage, ADDR msg
  3098.                      invoke DispatchMessage, ADDR msg
  3099.              .ENDW
  3100.              mov     eax,msg.wParam
  3101.              ret
  3102.      WinMain endp
  3103.  
  3104.      WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  3105.          LOCAL hdc:HDC
  3106.          LOCAL ps:PAINTSTRUCT
  3107.          LOCAL rect:RECT
  3108.          mov   eax,uMsg
  3109.          .IF eax==WM_DESTROY
  3110.              invoke PostQuitMessage,NULL
  3111.          .ELSEIF eax==WM_PAINT
  3112.              invoke BeginPaint,hWnd, ADDR ps
  3113.              mov    hdc,eax
  3114.              invoke GetClientRect,hWnd, ADDR rect
  3115.              invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
  3116.                      DT_SINGLELINE or DT_CENTER or DT_VCENTER
  3117.              invoke EndPaint,hWnd, ADDR ps
  3118.          .ELSE
  3119.              invoke DefWindowProc,hWnd,uMsg,wParam,lParam
  3120.              ret
  3121.          .ENDIF
  3122.          xor   eax, eax
  3123.          ret
  3124.      WndProc endp
  3125.      end start
  3126.  
  3127.  
  3128.  
  3129. The majority of the code is the same as the example in tutorial 3. I'll explain 
  3130. only the important changes.
  3131.  
  3132.     LOCAL hdc:HDC
  3133.     LOCAL ps:PAINTSTRUCT
  3134.     LOCAL rect:RECT
  3135.  
  3136. These are local variables that are used by GDI functions in our WM_PAINT 
  3137. section. hdc is used to store the handle to device context returned from 
  3138. BeginPaint call. ps is a PAINTSTRUCT structure. Normally you don't use the 
  3139. values in ps. It's passed to BeginPaint function and Windows fills it with 
  3140. appropriate values. You then pass ps to EndPaint function when you finish 
  3141. painting the client area. rect is a RECT structure defined as follows:
  3142.  
  3143.  
  3144.      RECT Struct
  3145.          left           LONG ?
  3146.          top           LONG ?
  3147.          right        LONG ?
  3148.          bottom    LONG ?
  3149.      RECT ends
  3150.  
  3151. Left and top are the coordinates of the upper left corner of a rectangle Right 
  3152. and bottom are the coordinates of the lower right corner. One thing to remember: 
  3153. The origin of the x-y axes is at the upper left corner of the client area. So 
  3154. the point y=10 is BELOW the point y=0.
  3155.  
  3156.         invoke BeginPaint,hWnd, ADDR ps
  3157.         mov    hdc,eax
  3158.         invoke GetClientRect,hWnd, ADDR rect
  3159.         invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
  3160.                 DT_SINGLELINE or DT_CENTER or DT_VCENTER
  3161.         invoke EndPaint,hWnd, ADDR ps
  3162.  
  3163. In response to WM_PAINT message, you call BeginPaint with handle to the window 
  3164. you want to paint and an uninitialized PAINTSTRUCT structure as parameters. 
  3165. After successful call, eax contains the handle to device context. Next you call 
  3166. GetClientRect to retrieve the dimension of the client area. The dimension is 
  3167. returned in rect variable which you pass to DrawText as one of its parameter. 
  3168. DrawText's syntax is:
  3169.  
  3170. int WINAPI DrawText(HDC hdc,
  3171.                                     LPCSTR lpString,
  3172.                                     int nCount,
  3173.                                     LPRECT lpRect,
  3174.                                     UNIT uFormat);
  3175.  
  3176. DrawText is a high-level text output API function. It handles some gory details 
  3177. such as word wrap, centering etc. so you could concentrate on the string you 
  3178. want to paint. Its low-level brother, TextOut, will be examined in the next 
  3179. tutorial. DrawText formats a text string to fit within the bounds of a 
  3180. rectangle. It uses the currently selected font,color and background (in the 
  3181. device context) to draw the text.Lines are wrapped to fit within the bounds of 
  3182. the rectangle. It returns the height of the output text in device units, in our
  3183. case, pixels. Let's see its parameters:
  3184.  
  3185.      hdc  handle to device context
  3186.  
  3187.      lpString  A pointer to the string you want to draw in the rectangle. 
  3188.      The string must be null-terminated else you would have to specify its 
  3189.      length in the next parameter, nCount.
  3190.     
  3191.      nCount  The number of characters to output. If the string is null-
  3192.      terminated, nCount must be -1. Otherwise nCount must contain the number of 
  3193.      characters in the string you want to draw.
  3194.  
  3195.      lpRect  A pointer to the rectangle (a structure of type RECT) you want to 
  3196.      draw the string in. Note that this rectangle is also a clipping rectangle, 
  3197.      that is, you could not draw the string outside this rectangle.
  3198.  
  3199.      uFormat The value that specifies how the string is displayed in the 
  3200.      rectangle. We use three values combined by "or" operator:
  3201.           DT_SINGLELINE  specifies a single line of text
  3202.           DT_CENTER  centers the text horizontally.
  3203.           DT_VCENTER centers the text vertically. Must be used with 
  3204.                      DT_SINGLELINE.
  3205.  
  3206. After you finish painting the client area, you must call EndPaint function to 
  3207. release the handle to device context.
  3208.  
  3209. That's it. We can summarize the salient points here:
  3210.  
  3211.      * You call BeginPaint-EndPaint pair in response to WM_PAINT message.
  3212.      * Do anything you like with the client area between the calls to 
  3213.        BeginPaint and EndPaint.
  3214.      * If you want to repaint your client area in response to other messages, 
  3215.        you have two choices:
  3216.           Use GetDC-ReleaseDC pair and do your painting between these calls
  3217.           Call InvalidateRect or UpdateWindow  to invalidate the entire client 
  3218.           area, forcing Windows to put WM_PAINT message in the message queue of
  3219.           your window and do your painting in WM_PAINT section
  3220.  
  3221.       [Reprinted With permission from Iczelion's Win32 Assembly HomePage]
  3222.                    http://203.148.211.201/iczelion/index.html
  3223.  
  3224.  
  3225. ::/ \::::::.
  3226. :/___\:::::::.
  3227. /|    \::::::::.
  3228. :|   _/\:::::::::.
  3229. :| _|\  \::::::::::.
  3230. :::\_____\:::::::::::........................THE.C.STANDARD.LIBRARY.IN.ASSEMBLY
  3231.                                                          The _Xprintf functions
  3232.                                                          by Xbios2
  3233.  
  3234.  
  3235. I. INTRODUCTION
  3236. ---------------
  3237. This is the second article I write on the C standard library, and perhaps some 
  3238. ask: "Why should this interest us?" or, more gently, "What's the philosophy 
  3239. behind these articles?". Well, here is why I write these articles:
  3240.  
  3241. - For C programmers that want to know what happens behind the HLL 'curtain'
  3242. - For asm programmers who wish to get ideas
  3243. - For asm programmers who need a C command but want to keep their code 'slim'
  3244.   (actually the code section is intended more as source to compile and use than
  3245.    source to read and understand, that's why it's not always well-commented in 
  3246.    a tutorial-like manner)
  3247. - For me, to better understand reverse-engineering and assembly coding.
  3248.  
  3249. Ok, now go for it....
  3250.  
  3251.  
  3252. II. WHAT C DOES
  3253. ---------------
  3254. How the various _printf functions (_Xprintf) work:
  3255.  
  3256. The _Xprintf functions call the ___vprinter function, with four parameters:
  3257. 1. output function address
  3258. 2. output function parameter
  3259. 3. pointer to format string
  3260. 4. pointer to arguments list
  3261.  
  3262. Parameter 1 is a pointer to a function that outputs the resulting string (to a 
  3263. file, stdout or to memory).
  3264. Parameter 2 is passed to the function pointed at by parameter 1, together with 
  3265. the string pointer and its length.
  3266. Parameter 3 is 'forwarded' by _Xprintf exactly as received by the user.
  3267. Parameter 4 is either 'forwarded' (by the _vXprintf functions) or points to the 
  3268. stack (for 'normal' _Xprintf functions).
  3269.  
  3270. Functions that send output to a file or to STDOUT also lock/unlock the stream. 
  3271. Besides that, all the 'dirty job' is passed to ___vprinter.
  3272.  
  3273.  
  3274. How ___vprinter works:
  3275. [the disassembly of ___vprinter would show this better, but is far too large]
  3276.  
  3277. 1. Read (next) char from format string
  3278. 2. If char is NUL, finish
  3279. 3. If char is not a '%', output it verbatim', loop back to [1]
  3280. 4. If char is '%' and next char is also '%', output a single '%' and loop to [1]
  3281. 5. Process the string up to a 'type_char'
  3282.    If everything is ok, output the result, loop to [1]
  3283.    If there is an unknown char, output the rest of the string verbatim, finish
  3284.  
  3285. It is interesting to notice how ___vprinter does it's output:
  3286.  
  3287. All output is performed character by character. To do this ___vprinter calls a
  3288. nother routine (let's call it _storechar) passing it two parameters: the 
  3289. character to store and a pointer to an 80-byte string in the stack of 
  3290. ___vprinter (actually in the C source that must have been a pointer to a local 
  3291. structure, because _storechar also modifies locals after those 80 bytes). 
  3292. _storechar writes the character in the sting and if the string is filled up, it 
  3293. calls a second function (call it _writestring) that calls the function whose 
  3294. pointer was passed to ___vprinter. Before returning, ___vprinter calls 
  3295. _writestring directly to output whatever bytes where left. _writestring is also 
  3296. responsible for setting a flag that will cause ___vprinter (and consequently 
  3297. _Xprintf) to return -1 instead of the number of chars output.
  3298.  
  3299. This way to perform output has the advantage of printing long strings without 
  3300. allocating much memory, while printing small strings using the output function 
  3301. only once. Actually this is the only advantage it has. Even if this solution 
  3302. was written well (which is _not_), it would still be awful in _sprintf and 
  3303. _vsprintf. In _(v)sprintf chars are written in the local buffer first, then, 
  3304. when this fills up, the second function (_writestring) is called, which calls 
  3305. a third function (included in the same .OBJ file with _sprintf) which finally 
  3306. calls _memcpy. With careful re-writing of sprintf, this could be achieved just 
  3307. by a simple, one-byte 'stosb'. Then printf and fprintf could be implemented 
  3308. atop sprintf. The problem here is that those functions should 'know' how much 
  3309. buffer space to allocate. Maybe the solution to this could be to leave 
  3310. allocating buffers to the user, by just giving a sprintf function (actually 
  3311. Microsoft thought this before me, and they give only wsprintf and wvsprintf 
  3312. in the Win32 API).
  3313.  
  3314. This article will actually focus on a vsprintf function, with all the format 
  3315. specifiers in Borland C (EXCEPT floating point numbers, which would (and maybe 
  3316. will) require a separate article. Also keep in mind that UNIX has a rather more 
  3317. complicated Xprintf set, which I'm glad to ignore :)
  3318.  
  3319.  
  3320. III. SOME COMMENTS ON THE CODE
  3321. -----------------------------
  3322. This is not exactly 'clear' code. This is because it was not written from 
  3323. scratch, but is the result of hand-optimization applied to the disassembly of 
  3324. ___vprinter (Actually Borland could sue me for this, but they'd really have a 
  3325. hard time trying to show that my code resembles theirs :)). That is, starting 
  3326. from an uncomprehensible but working source code, I kept changing the source 
  3327. code and compiling until I got a better source code (yet still uncomprehensible 
  3328. :). That's also a reason the code is poorly commented. Anyway if you're just 
  3329. interested in a simple _sprintf function, skip to the code section. For the 
  3330. curious, here are some differences my version has:
  3331.  
  3332. - A self-contained procedure
  3333.   That is, there is only a _sprintf function, which calls nothing, while
  3334.   _sprintf involves: ___vprinter, ___longtoa, ___strlen, plus three other
  3335.   functions called by ___vprinter (_storechar, _writestring and another one
  3336.   that converts pointers into hex)
  3337. - Much smaller code
  3338. - Much less stack used
  3339. - Probably faster code (actually it is not a speed-optimized version, but yet 
  3340.   it must be much faster)
  3341. - It's home-made, and brand-new :)
  3342.  
  3343.  
  3344. IV. THE CODE
  3345. -------------
  3346. Well, as I said, you're not expected to understand it at once. Yet, if you 
  3347. insist, read and enjoy...
  3348.  
  3349. ; sprintf.asm ============================================================
  3350. .386
  3351. .model flat
  3352.  
  3353. getarg    macro register
  3354.     lea    eax, [a_argList]
  3355.     mov    edx, [eax]
  3356.     add    dword ptr [eax], 4
  3357.     mov    register, [edx]
  3358.     endm
  3359.  
  3360. .data
  3361. Null        db '(null)',0
  3362.         align 4
  3363. jumptable    dd offset BlankOrPlus    ; 0
  3364.         dd offset HashSign    ; 1
  3365.         dd offset Asterisk    ; 2
  3366.         dd offset MinusSign    ; 3
  3367.         dd offset Dot        ; 4
  3368.         dd offset Digit        ; 5
  3369.         dd offset h_shortint    ; 6
  3370.         dd offset d_decimal    ; 7
  3371.         dd offset o_octal    ; 8
  3372.         dd offset u_unsigned    ; 9
  3373.         dd offset x_Hexadecimal    ; 10
  3374.         dd offset p_pointer    ; 11
  3375.         dd offset unknown    ; 12 = f_floating
  3376.         dd offset c_char    ; 13
  3377.         dd offset s_string    ; 14
  3378.         dd offset n_CharsWritten ; 15
  3379.         dd offset formatLoop    ; 16 = Ignore character
  3380.         dd offset unknown    ; 17 = Unknown char
  3381.         dd offset Percent    ; 18
  3382.  
  3383.     ;       !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /  
  3384. xxlat    db  0, 17, 17,  1, 17, 18, 17, 17, 17, 17,  2,  0, 17,  3,  4, 17
  3385.     ;   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
  3386.     db  5,  5,  5,  5,  5,  5,  5,  5,  5, 17, 17, 17, 17, 17, 17, 17
  3387.     ;   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O  
  3388.     db 17, 17, 17, 17, 17, 12, 16, 12,  8, 17, 17, 17, 16, 17, 16, 17
  3389.     ;   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
  3390.     db 17, 17, 17, 17, 17, 17, 17, 17, 10, 17, 17, 17, 17, 17, 17, 17
  3391.     ;   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
  3392.     db 17, 17, 17, 13,  7, 12, 12, 12,  6,  7, 17, 17, 16, 17, 15,  8
  3393.     ;   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~ DEL
  3394.     db 11, 17, 17, 14, 17,  9, 17, 17, 10, 17, 17, 17, 17, 17, 17, 17
  3395.  
  3396.  
  3397. .code
  3398. _vsprintf    proc C near uses ebx edi esi, a_output:dword, a_format:dword, \
  3399.                                               a_argList:dword
  3400.  
  3401.         local v_width:dword, v_prec:dword, v_zeroLen:dword, \
  3402.                       v_sign:dword, v_strbuf:byte:12, v_strLen:dword
  3403.  
  3404.         mov    esi, [a_format]
  3405.         mov    edi, [a_output]
  3406.  
  3407. mainLoop:    lodsb                ; get character
  3408.         cmp    al, '%'            ; test if it is '%'
  3409.         je    short controlChar    
  3410.         stosb                ; if not, just copy it
  3411.         test    al, al
  3412.         jnz    short mainLoop        ; if char is not NULL, loop
  3413.         jmp    EndOfString        ; jump if char is null
  3414. ; ---------------------------------------------------------------------------
  3415.  
  3416. controlChar:    xor    ecx, ecx        ; set stage to 0
  3417.         or    eax, -1
  3418.         xor    ebx, ebx        ; no flags set
  3419.         mov    [v_width], eax        ; no width given
  3420.         mov    [v_zeroLen], ecx    ; 0
  3421.         mov    [v_prec], eax        ; no .prec given
  3422.         mov    [v_sign], ecx        ; 0, no sign prefix
  3423.  
  3424. formatLoop:    xor    eax, eax
  3425.         lodsb
  3426.         cmp    al, ' '
  3427.         jl    unknown            ; char below ' '
  3428.         movzx    edx, byte ptr xxlat - ' '[eax]
  3429.         jmp    jumptable[edx*4]    ; we jump with the char in AL
  3430. ; ---------------------------------------------------------------------------
  3431. n_CharsWritten:    getarg    eax
  3432.         mov    edx, edi
  3433.         sub    edx, [a_output]        ; calculate length
  3434.  
  3435.         test    ebx, 16
  3436.         jnz    short nchars_short
  3437.  
  3438.         mov    [eax], edx
  3439.         jmp    short fw_mainloop
  3440.  
  3441. nchars_short:    mov    [eax], dx
  3442. fw_mainloop:    jmp    mainLoop
  3443. ; ---------------------------------------------------------------------------
  3444. Percent:    cmp    byte ptr [esi-2], al    ; al='%'
  3445.         jne    unknown
  3446.         stosb
  3447.         jmp    mainLoop        
  3448. ; ---------------------------------------------------------------------------
  3449. ; flag characters
  3450. HashSign:    or    ebx, 1
  3451.         jmp    short chkflags
  3452. MinusSign:    or    ebx, 2
  3453.         jmp    short chkflags
  3454. BlankOrPlus:    or    byte ptr [v_sign], al    ; ' ' will become '+'
  3455. chkflags:    or    ecx, ecx
  3456.         jnz    unknown
  3457.         jmp    formatLoop
  3458. ; ---------------------------------------------------------------------------
  3459. Asterisk:    getarg    eax
  3460.  
  3461.         cmp    ecx, 2
  3462.         jge    short asterisk_prec
  3463.         test    eax, eax
  3464.         jge    short width_positive
  3465.         neg    eax
  3466.         or    ebx, 2
  3467.  
  3468. width_positive:    mov    [v_width], eax
  3469.         mov    ecx, 3
  3470.         jmp    short fwwB
  3471. ; - - - - - - - - - - - - - - - - - - - - - - -
  3472. asterisk_prec:    cmp    ecx, 4
  3473.         jnz    unknown
  3474.         inc    ecx            ; set stage to 5
  3475.         mov    [v_prec], eax
  3476.  
  3477. fwwB:        jmp    formatLoop
  3478. ; ---------------------------------------------------------------------------
  3479. Dot:        cmp    ecx, 4
  3480.         jge    unknown
  3481.         mov    ecx, 4
  3482.         inc    [v_prec]        ; set .prec to 0
  3483.         jmp    formatLoop
  3484. ; ---------------------------------------------------------------------------
  3485. Digit:        sub    al, '0'            ; convert ASCII to value
  3486.         jnz    short digit2
  3487.         or    ecx, ecx
  3488.         jnz    short digit2
  3489.         test    ebx, 2            ; we come here if width=0n
  3490.         jnz    short fwwC
  3491.         or    ebx, 8
  3492.         inc    ecx            ; set stage to 1
  3493.         jmp    fwwC
  3494. ; - - - - - - - - - - - - - - - - - - - - - - -
  3495. digit2:        cmp    ecx, 2
  3496.         jg    short digit_prec
  3497.         mov    ecx, 2
  3498.         cmp    [v_width], 0
  3499.         jge    short digit_width
  3500.         mov    [v_width], eax
  3501.         jmp    short fwwC
  3502. ; - - - - - - - - - - - - - - - - - - - - - - -
  3503. digit_width:    imul    edx, [v_width], 10
  3504.         add    eax, edx
  3505.         mov    [v_width], eax
  3506.         jmp    short fwwC
  3507. ; - - - - - - - - - - - - - - - - - - - - - - -
  3508. digit_prec:    cmp    ecx, 4
  3509.         jnz    unknown
  3510.         imul    edx, [v_prec], 10
  3511.         add    eax, edx
  3512.         mov    [v_prec], eax
  3513.  
  3514. fwwC:        jmp    formatLoop
  3515. ; ---------------------------------------------------------------------------
  3516. h_shortint:    or    ebx, 16
  3517.         mov    ecx, 5
  3518.         jmp    formatLoop
  3519. ; ---------------------------------------------------------------------------
  3520. o_octal:    mov    ecx, 8            ; radix
  3521.         test    ebx, 1
  3522.         jz    short unsigned
  3523.         mov    byte ptr [v_sign], '0'
  3524.         jmp    short integer
  3525.  
  3526. u_unsigned:    mov    ecx, 10            ; radix
  3527. unsigned:    mov    byte ptr [v_sign], 0    ; no sign
  3528.         jmp    short integer
  3529.  
  3530. x_Hexadecimal:    mov    ecx, 16            ; radix
  3531.         mov    ah, al
  3532.         xor    al, 'X'            ; AL is the char ('x' or 'X')
  3533.         mov    bh, al
  3534.         test    ebx, 1
  3535.         jz    short integer
  3536.         mov    al, '0'
  3537.         mov    word ptr [v_sign], ax
  3538.         jmp    short integer
  3539.  
  3540. d_decimal:    mov    ecx, 10            ; radix
  3541.         or    ebx, 32
  3542.  
  3543. integer:    getarg    eax
  3544.         test    ebx, 16
  3545.         jz    short integer_cnvt    ; if not short, don't change
  3546.  
  3547. short_integer:    test    ebx, 32            ; is integer signed?
  3548.         jnz    short short_signed
  3549.         and    eax, 0FFFFh        ; zero extend 16 to 32
  3550.         jmp    short nosign
  3551. short_signed:    cwde                ; sign extend 16 to 32
  3552.  
  3553. integer_cnvt:    test    ebx, 32
  3554.         jz    nosign
  3555.         or    eax, eax
  3556.         jns    nosign
  3557.         neg    eax
  3558.         mov    byte ptr [v_sign], '-'
  3559.  
  3560. nosign:        lea    edx, [offset v_strbuf + 11]
  3561.         or    eax, eax
  3562.         jnz    short ltoa
  3563.         cmp    [v_prec], eax        ; eax is 0 if we are here
  3564.         jnz    short zero
  3565.         mov    byte ptr [edx], al    ; value 0 with .0 prec
  3566.         mov    [v_strLen], eax        ; means no string
  3567.         jmp    printit            ; so output no digits
  3568.  
  3569. zero:        cmp    byte ptr [v_sign], '0'
  3570.         jnz    short ltoa
  3571.         mov    byte ptr[v_sign], 0    ; we don't want 0x0, nor '00'
  3572.  
  3573.     ; convert EAX into ASCII
  3574. ltoa:        push    edi
  3575.         push    esi
  3576.         xor    esi, esi
  3577.         mov    edi, edx
  3578.         mov    byte ptr [edi], 0
  3579.          
  3580. ltoaLoop:    xor    edx, edx
  3581.         div    ecx            ; ecx is the radix
  3582.         xchg    eax, edx
  3583.         add    al,90h
  3584.         daa
  3585.         adc    al,40h
  3586.         daa
  3587.         or    al, bh            ; switch case if needed
  3588.         dec    edi
  3589.         inc    esi
  3590.         mov    [edi], al
  3591.         xchg    eax, edx
  3592.         or    eax, eax
  3593.         jnz    short ltoaLoop
  3594.  
  3595.         mov    eax, esi
  3596.         mov    edx, edi
  3597.         pop    esi
  3598.         pop    edi
  3599.  
  3600.         mov    [v_strLen], eax
  3601.         mov    ecx, [v_prec]
  3602.         or    ecx, ecx
  3603.         js    noprec
  3604.  
  3605.     ; A precision was given
  3606.         sub    ecx, eax
  3607.         jle    short skipzerolen
  3608.         mov    [v_zeroLen], ecx    ; if prec>digits then
  3609.                         ; add (prec-digits) '0' 
  3610.         jmp    short skipzerolen
  3611.  
  3612. noprec:        test    ebx, 8
  3613.         jz    short skipzerolen
  3614.         cmp    [v_width], 0
  3615.         jle    short skipzerolen
  3616.  
  3617. ;------------------
  3618. ; we come here if width=0n
  3619.         mov    ecx, [v_width]
  3620.         sub    ecx, eax        ; EAX=[v_strLen]
  3621.         jle    short skipzerolen
  3622.         mov    eax, dword ptr [v_sign]
  3623.         or    al, al
  3624.         jz    short setzerolen
  3625.         dec    ecx
  3626.         shr    eax, 8
  3627.         jz    short setzerolen
  3628.         dec    ecx
  3629.         js    short skipzerolen
  3630. setzerolen:    mov    [v_zeroLen], ecx
  3631.  
  3632. skipzerolen:    mov    eax, dword ptr [v_sign]
  3633.         or    al, al
  3634.         jz    short finishint
  3635.         dec    [v_width]
  3636.         shr    eax, 8
  3637.         jz    short finishint
  3638.         dec    [v_width]
  3639.  
  3640. finishint:    mov    eax, [v_zeroLen]
  3641.         add    [v_strLen], eax
  3642.         jmp    printit
  3643.  
  3644. ; ---------------------------------------------------------------------------
  3645. ; Pointer: same as %.8X
  3646.  
  3647. p_pointer:    getarg    ecx
  3648.  
  3649.         lea    edx, [v_strbuf]
  3650.         push    ebx
  3651.         mov    ebx, 7
  3652. loopPointer:    mov    al, cl
  3653.         shr    ecx, 4
  3654.         and    al, 0Fh
  3655.         add     al,90h
  3656.         daa
  3657.         adc     al,40h
  3658.         daa
  3659.         mov    [edx+ebx], al
  3660.         dec    ebx
  3661.         jns    loopPointer
  3662.         pop    ebx
  3663.  
  3664.         mov    byte ptr [edx+8], 0
  3665.         mov    [v_strLen], 8
  3666.         jmp    printit
  3667. ; ---------------------------------------------------------------------------
  3668. c_char:        getarg    eax
  3669.         lea    edx, [v_strbuf]
  3670.         mov    [edx], eax        ; stores char (rest of EAX is
  3671.                         ; not important)
  3672.         mov    [v_strLen], 1        ; set length to one char
  3673.         jmp    printit
  3674. ; ---------------------------------------------------------------------------
  3675. s_string:    getarg    edx
  3676.         or    eax, -1
  3677.         test    edx, edx
  3678.         jnz    short strlen_I
  3679.         mov    edx, offset Null    ; Pointer 0 prints 'Null'
  3680. strlen_I:    inc    eax
  3681.         cmp    byte ptr [edx+eax], 0
  3682.         jnz    short strlen_I
  3683.  
  3684.         cmp    eax, [v_prec]
  3685.         jle    short setLen
  3686.         cmp    [v_prec], 0
  3687.         jl    short setLen
  3688.         mov    eax, [v_prec]
  3689.  
  3690. setLen:        mov    [v_strLen], eax
  3691.  
  3692. ; ---------------------------------------------------------------------------
  3693. ; we must arrive here with EDX pointing to the string to print
  3694. ; and it's length in [v_strLen]
  3695.  
  3696.     ; left pad with spaces IF necessary
  3697. printit:    test    ebx, 2            ; Is it left justified?
  3698.         mov    ebx, [v_width]
  3699.         jnz    short printPrefix    ; if yes, don't pad left
  3700.         mov    ecx, ebx
  3701.         sub    ecx, [v_strLen]
  3702.         jle    printPrefix
  3703.         mov    al, ' '
  3704.         rep stosb            ; >>> left pad
  3705.         mov    ebx, [v_strLen]
  3706.  
  3707.     ; print one- or two-chars PREFIX
  3708. printPrefix:    mov    eax, [v_sign]
  3709.         or    al, al
  3710.         jz    short padZero
  3711.         stosb                ; print the sign prefix
  3712.         shr    eax, 8            ; AL=AH, AH=0
  3713.         jz    short padZero
  3714.         stosb                ; print the sign prefix
  3715.  
  3716.     ; pad with zeroes IF necessary
  3717. padZero:    mov    ecx, [v_zeroLen]    ; we are sure that ecx>=0
  3718.         sub    [v_strLen], ecx
  3719.         sub    ebx, ecx
  3720.         mov    al, '0'            ; ECX=[v_zeroLen]
  3721.         rep stosb            ; >>> pad with 0s
  3722.         mov    ecx, [v_strLen]
  3723.         sub    ebx, ecx
  3724.         xchg    esi, edx
  3725.         rep movsb            ; >>> copy string
  3726.         xchg    esi, edx
  3727.         js    short skipRightpad    ; refers to SUB EBX, ECX
  3728.         mov    ecx, ebx
  3729.         mov    al, ' '
  3730.         rep stosb            ; >>> right pad with ' '
  3731. skipRightpad:    jmp    mainLoop
  3732. ; ---------------------------------------------------------------------------
  3733. ;
  3734. ; If an    unknown    specification character is found, _vsprintf enters the
  3735. ; following loop. This loop copies verbatim all the rest of the string
  3736. ; (from the '%' on)
  3737.  
  3738. unknown:    mov    al, '%'
  3739. scanback:    dec    esi
  3740.         cmp    [esi], al
  3741.         jne    short scanback
  3742. copyrest:    lodsb
  3743.         stosb
  3744.         test    al, al
  3745.         jnz    short copyrest
  3746. ;
  3747. ; ---------------------------
  3748. ; return the number of chars written
  3749.  
  3750. EndOfString:    mov    eax, edi
  3751.         sub    eax, [a_output]
  3752.         dec    eax
  3753.         ret
  3754. endp
  3755.  
  3756. ends
  3757. end
  3758. ; EOF ====================================================================
  3759.  
  3760.  
  3761. ::/ \::::::.
  3762. :/___\:::::::.
  3763. /|    \::::::::.
  3764. :|   _/\:::::::::.
  3765. :| _|\  \::::::::::.
  3766. :::\_____\:::::::::::............................................THE.UNIX.WORLD
  3767.                                          X-Windows in Assembly Language: Part I
  3768.                                          by mammon_
  3769.  
  3770.  
  3771. The sensible way to write programs for X-Windows is to use a toolkit such as Xt
  3772. or Gtk; the easy way would be to use a scripting package such as Python or 
  3773. Tcl/Tk. Modern assembly language coders, however, are known for sacrificing 
  3774. ease and sensibility in the name of curiosity and execution speed; it is in 
  3775. this spirit that the potential for programming X-Windows in assembly language 
  3776. will now be investigated.
  3777.  
  3778.  
  3779. X-Windows Programming
  3780. ---------------------
  3781. Like other GUIs, X-Windows uses an event-driven programming style in which an 
  3782. application registers itself with the system, displays its main user interface, 
  3783. and waits for system events signalling that the user has interacted with the 
  3784. program. There are four main 'levels' of X-Windows Programming: XProtocol, 
  3785. XLib, Xt or 'toolkit' programming, and scripting.
  3786.  
  3787. XProtocol
  3788. X-Windows consists of the X Server which handles graphics output, keyboard and 
  3789. mouse input, event signalling, and commands sent from client programs (Window 
  3790. Managers, applications). Clients communicate with the X Server using XProtocol, 
  3791. which consists of byte streams exchanged between the client and the server -- 
  3792. in a sense, like the packets that a network client exchanges with a network
  3793. server. XProtocol is virtually useless for application programming, for the 
  3794. coding overhead for each server request makes development impractical. The 
  3795. details of XProtocol requests can be found in '/usr/include/X11/Xproto.h'.
  3796.  
  3797. XLib
  3798. The equivalent of the Win32 API in X-Windows is XLib. Even if one uses toolkits
  3799. for application coding, there is no way to escape XLib coding. XLib serves as 
  3800. an interface between the client programs and the X Server; essentially, it is a 
  3801. library of XProtocol functions exported for use by applications.
  3802.  
  3803. Xt
  3804. Toolkit programming is similar to using class libraries (like MFC, OWL, or VCL)
  3805. on the Win32 platform. There are a number of toolkits available, such as Qt, 
  3806. Gtk, Xt Intrinsics, Athena, and the Motif toolkit. Each toolkit consists of 
  3807. extensible widgets (like resources in Win32) that define basic window types: 
  3808. buttons, scrollbars, dialogs, edit windows, etc.
  3809.  
  3810. Scripting
  3811. A wide variety of scripting languages are available for the Unix platorm, and 
  3812. many of these have windowing toolkits that enable them to produce X-Windows 
  3813. applications. The most popular are Tcl/Tk, Python, and Java; needless to say, 
  3814. these programming methods may not be implemented in assembly language.
  3815.  
  3816.  
  3817. The XLib Programming Model
  3818. --------------------------
  3819. An application written for the XLib interface demonstrates the basic principles 
  3820. of X-Windows programming as a whole. These principles make up a 5-step method:
  3821.  
  3822. Step I : Connect to the Display
  3823. The first step of an X-Windows application is also the most simple: a call is 
  3824. made to XOpenDisplay; the result --returned in eax of course-- is a pointer to 
  3825. a Display structure. This should be saved, as it will be required for just 
  3826. about every subsequent call: 
  3827.   p_disp = XOpenDisplay( NULL );
  3828. Note: I am providing the sample source in C for this section; the assembler 
  3829. reconstruction will be presented later.
  3830.  
  3831. Step II : Initialize Application Resources (Colors and Fonts)
  3832. Before a window can be displayed, it requires a Graphic Context (similar to the 
  3833. Win32 DC); before the GC can be created, it requires that the colors and fonts 
  3834. to be used by the window be initialized.
  3835.  
  3836. The simplest way to do this is to use the XLoadQueryFont and the WhitePixel and 
  3837. BlackPixel macros: 
  3838.   mfontstruct = XLoadQueryFont( p_disp, "fixed");
  3839.   WhitePix = WhitePixel( p_disp, DefaultScreen(p_disp));
  3840.   BlackPix = BlackPixel( p_disp, DefaultScreen(p_disp));
  3841. Once again, the values are saved for later use. Note that a more complex method 
  3842. of allocating colors will be used in the assembly code later; there, a handle 
  3843. to the default X Windows colormap is obtained via a call to XDefaultColormap, 
  3844. and XAllocNamedColor is used to allocate white and black pixel values: this 
  3845. accomplishes the same as the above code, but without using the macros.
  3846.  
  3847. Step III : Create Window(s)
  3848. There are four things that must be done to create a window: the window itself 
  3849. is registered with the X Server and given a Resource ID, the GC is registered 
  3850. with the X Server and given its own Resource ID, the window must specify which 
  3851. events it will respond to, and finally the window must be mapped into the X 
  3852. display.
  3853.  
  3854. Creating the window requires a call to XCreateWindow or XCreateSimpleWindow. 
  3855. XCreateSimpleWindow, used below, requires the display, parent window, x and y 
  3856. screen coordinates, window width and height, border width, border pixel value, 
  3857. and background pixel value. XCreateWindow, used in the assembly version, is
  3858. passed the display, parent window, x & y, width & height, border width, color 
  3859. depth, window class, visual attribute, value mask, and an XSetWindowAttributes 
  3860. structure. A handle to the created window is returned.
  3861.   Main = XCreateSimpleWindow( p_disp, DefaultRootWindow( p_disp ), 100, 100,
  3862.                        100, 50, 1, BlackPix, WhitePix);
  3863. Creating a GC is not strictly necessary; however doing without one causes the 
  3864. application appearance to be unpredicatable (I found that the background of my 
  3865. window became transparent). A GC is created by calling XCreateGC, which is 
  3866. passed the display, window handle, value mask, and a GraphicsContextValues 
  3867. structure:
  3868.   theGC = XCreateGC(p_disp, Main,(GCFont | GCForeground | GCBackground), &gcv);
  3869. Input events are selected using the XSelectInput function, which is passed the 
  3870. display, window handle, and the ORed values of event masks:
  3871.   XSelectInput( p_disp, Main, ExposureMask );
  3872. Finally, the window is mapped onto the display (and therefore displayed) with 
  3873. the XMapWindow call, which is relatively self-explanatory:
  3874.   XMapWindow( p_disp, Main );
  3875.  
  3876. At this point, the procedure must be created for each child window (buttons, 
  3877. scrollbars, etc); the following shows the creation of a button with its own GC,
  3878. and selection of the Exposure and ButtonPress event masks:
  3879.   Exit = XCreateSimpleWindow(p_disp, Main, 15, 1, 60, 15, 1,
  3880.                              WhitePix, BlackPix);
  3881.   XSelectInput(p_disp, Exit, ExposureMask | ButtonPressMask );
  3882.   XMapWindow(p_disp, Exit);
  3883.   exitGC = XCreateGC(p_disp, Exit,(GCFont | GCForeground | GCBackground),&gcv);
  3884. Note that a separate GC is not needed for each window if they will be sharing 
  3885. the same background, foreground, and font colors.
  3886.  
  3887. Step IV : Event Loop
  3888. The event loop is the 'meat' of the program, where the application responds to 
  3889. user events. This loop calls XNextEvent to get the next system event, and 
  3890. responds to the ones sent to its windows. The following loop catches the Expose
  3891. event and draws text into each window using XDrawString on the initial exposure 
  3892. of each window (xexpose.count ==0). In addition, when the Exit button is 
  3893. pressed, the while loop exits and the application terminates.
  3894.   while( !Done ){
  3895.     XNextEvent(p_disp, &theEvent);
  3896.     if( theEvent.xany.window == Main){
  3897.       if( theEvent.type == Expose && theEvent.xexpose.count == 0){
  3898.         XDrawString(p_disp, Main, theGC, 1, 40, msgtext, strlen(msgtext));
  3899.       }  }
  3900.     if( theEvent.xany.window == Exit){
  3901.       switch(theEvent.type){
  3902.        case Expose:
  3903.          if( theEvent.xexpose.count == 0){
  3904.            XDrawString(p_disp, Exit, exitGC, 2, 11, extext, strlen(extext) );
  3905.          }
  3906.          break;
  3907.        case ButtonPress:
  3908.          Done = 1; }  }  }
  3909.  
  3910. Step V : Clean Up and Close Display
  3911. At this point the application is over; the various handles must be freed, the 
  3912. windows destroyed, and the display closed. The functions typically used for 
  3913. this are demonstrated below:
  3914.   XFreeGC(p_disp, theGC);
  3915.   XFreeGC(p_disp, exitGC);
  3916.   XUnloadFont(p_disp, mfontstruct->fid);
  3917.   XDestroyWindow(p_disp, Main);
  3918.   XCloseDisplay(p_disp);
  3919.   exit(0);
  3920.  
  3921. Note that sll of the functions, structures, and messages used above are defined 
  3922. in '/usr/include/X11/Xlib.h', './X11/Xutil.h' and './X11/X.h'.
  3923.  
  3924.  
  3925. Inline Assembler With GCC
  3926. -------------------------
  3927. Due to the presence of the GAS assembler within GCC, inline assembler is pretty
  3928. straightforward. In GCC, the 'asm' keyword is used to prefix a block of asm 
  3929. instructions; the format of 'asm' is as follows:
  3930.    asm( statements : output vars : input vars : modified registers);
  3931. Note that the last three parameters are usually used only if you are writing an 
  3932. entire function in assembly language, or if you are modifying registers that 
  3933. you do not save (it is better to save all the registers that you will modify, 
  3934. if they contain values that will be needed later).
  3935.  
  3936. The asm statements are passed directly to GAS, and thus they need to be in a 
  3937. format that GAS will recognize. For this reason, multiline asm statements will 
  3938. require a newline (and, optionally, a tab) after each statement, like so:
  3939.  
  3940.   asm(  "
  3941.         statement1 \n
  3942.         statement2 \n
  3943.         statement3 \n
  3944.         statement4"
  3945.     : "g" (outvar)
  3946.     : "g" (invar)
  3947.     : eax, ebx, ecx
  3948.     );
  3949. or, as I have used below:
  3950.   asm(  "statement1 \n\t"
  3951.         "statement2 \n\t"
  3952.         "statement3 \n\t"
  3953.         "statement4 \n\t");
  3954. Other than that there are no real restrictions. Structures do not pass well 
  3955. between C and GAS; if you need to reference specific structure variables from 
  3956. inline assembly code, it is better to place those variables into temporary C 
  3957. variables, whcih can then be accessed from the assembler block as normal. The 
  3958. following demonstrates this:
  3959.     fid = mfontstruct->fid;
  3960.     asm(    "
  3961.             push fid\n
  3962.             push mainGC\n
  3963.             push p_disp\n
  3964.             call XSetFont\n
  3965.             add $12, %esp");
  3966. More information on the GCC inline assembler can be found at: 
  3967. Avly's Programming Page (http://www.castle.net/~avly/djasm.html)
  3968. CodeX Software (http://www.gameprog.com/codex/tut/att_asm.html)
  3969. Brennan's DGPP Resources (http://brennan.home.ml.org/djgpp/) [Currently Down]
  3970.  
  3971.  
  3972. The XHell Sample Program
  3973. ------------------------
  3974. In order to be able to use the C header files for X-Windows, the following 
  3975. program has been written in C for GCC, using C code for the data declarations 
  3976. and assembler for the 'meat' of the program. In Part II of this article (next 
  3977. issue) I will convert this program to the Xt model and implement it in NASM.
  3978. // xhell.c ============================================================
  3979. #include <X11/Xlib.h>
  3980. #include <X11/Xutil.h>
  3981. /* ==================== Global Variable Declarations ===================== */
  3982. char    *msgtext = "You are in XHell",
  3983.         *extext = "Exit XHell",
  3984.         *m_font = "fixed",
  3985.         *app_name = "xhello",
  3986.         *window_title = "XHell",
  3987.         *szWhite = "white",
  3988.         *szBlack = "black";
  3989. XFontStruct *mfontstruct;
  3990. Display *p_disp;
  3991. Window Main, Exit;
  3992. GC mainGC, exitGC;
  3993. XEvent theEvent;
  3994. Font fid;
  3995. Colormap cmap;
  3996. int Done = 0;
  3997. unsigned long pxBlack, pxWhite;
  3998. XSetWindowAttributes xswa;
  3999. XColor pixBlack, pixWhite;
  4000. XGCValues gcv;
  4001. /* ================ Start of Main Function ==================== */
  4002. main()
  4003. {
  4004. /* ===== Connect to Display ===== */
  4005.   asm(  "push $0\n\t"
  4006.           "call XOpenDisplay\n\t"
  4007.           "movl %eax, p_disp\n\t"
  4008.           "add $4, %esp\n\t");
  4009. /* ===== Setup Colors n' Fonts ===== */
  4010.     asm( "push m_font\n\t"
  4011.           "push p_disp\n\t"
  4012.           "call XLoadQueryFont\n\t"
  4013.           "add $8, %esp\n\t"
  4014.           "movl %eax, mfontstruct");
  4015. /* ===== Prepare Main Window ===== */
  4016.     fid = mfontstruct->fid;
  4017. /* ===== Create Main Graphics Context ===== */
  4018.     // Obtain Colormap Handle
  4019.     asm(    "push p_disp\n\t"
  4020.             "call XDefaultScreen\n\t"
  4021.             "add $4, %esp\n\t"
  4022.             "push %eax\n\t"
  4023.             "push p_disp\n\t"
  4024.             "call XDefaultColormap\n\t"
  4025.             "add $8, %esp\n\t"
  4026.             "movl %eax, cmap");
  4027.     // Allocate White and Black Colors
  4028.     asm(    "push $pixWhite\n\t"
  4029.             "push $pixWhite\n\t"
  4030.             "push szWhite\n\t"
  4031.             "push cmap\n\t"
  4032.             "push p_disp\n\t"
  4033.             "call XAllocNamedColor\n\t"
  4034.             "add $20, %esp");
  4035.     asm(    "push $pixBlack\n\t"
  4036.             "push $pixBlack\n\t"
  4037.             "push szBlack\n\t"
  4038.             "push cmap\n\t"
  4039.             "push p_disp\n\t"
  4040.             "call XAllocNamedColor\n\t"
  4041.             "add $20, %esp");
  4042.   xswa.background_pixel = pixWhite.pixel;
  4043.   asm(    "push $xswa\n\t"
  4044.             "movl $1, %ebx\n\t"
  4045.             "shl $1, %ebx\n\t"        //CWBackPixel = 1 << 1
  4046.             "push %ebx\n\t"
  4047.             "push $0\n\t"            //CopyFromParent = 0 (X.h)
  4048.             "push $1\n\t"            //InputOutput = 1 (X.h)
  4049.             "push $0\n\t"            //CopyFromParent = 0 (X.h)
  4050.             "push $1\n\t"
  4051.             "push $50\n\t"
  4052.             "push $100\n\t"
  4053.             "push $100\n\t"
  4054.             "push $100\n\t"
  4055.             "push p_disp\n\t"
  4056.             "call XDefaultRootWindow\n\t"
  4057.             "add $4, %esp\n\t"
  4058.             "push %eax\n\t"
  4059.             "push p_disp\n\t"
  4060.             "call XCreateWindow\n\t"
  4061.             "add $48, %esp\n\t"
  4062.             "movl %eax, Main");
  4063.       gcv.font = fid;
  4064.     asm(    "push $gcv\n\t"
  4065.             "movl $1, %ebx\n\t"
  4066.               "shl $14, %ebx\n\t"        //GCFont = 1 << 14
  4067.               "push %ebx\n\t"
  4068.             "push  Main\n\t"
  4069.             "push p_disp\n\t"
  4070.             "call XCreateGC\n\t"
  4071.             "add $16, %esp\n\t"
  4072.             "movl %eax, mainGC");
  4073.     pxBlack = pixBlack.pixel;
  4074.     pxWhite = pixWhite.pixel;
  4075.     asm(    "push fid\n\t"
  4076.             "push mainGC\n\t"
  4077.             "push p_disp\n\t"
  4078.             "call XSetFont\n\t"
  4079.             "push pxBlack\n\t"
  4080.             "push mainGC\n\t"
  4081.             "push p_disp\n\t"
  4082.             "call XSetForeground\n\t"
  4083.             "push pxWhite\n\t"
  4084.             "push mainGC\n\t"
  4085.             "push p_disp\n\t"
  4086.             "call XSetBackground\n\t"
  4087.             "add $36, %esp");
  4088.     asm(     "movl $1, %ebx\n\t"
  4089.               "shl $15, %ebx\n\t"        //ExposureMask = 1 << 15
  4090.               "push %ebx\n\t"
  4091.             "push Main\n\t"
  4092.             "push p_disp\n\t"
  4093.             "call XSelectInput\n\t"
  4094.             "add $12, %esp");
  4095.     asm(    "push Main\n\t"
  4096.             "push p_disp\n\t"
  4097.             "call XMapWindow\n\t"
  4098.             "add $8, %esp");
  4099. /* ===== Create Child Windows ===== */
  4100.     asm(    "push pxWhite\n\t"
  4101.             "push pxBlack\n\t"
  4102.             "push $1\n\t"
  4103.             "push $15\n\t"
  4104.             "push $60\n\t"
  4105.             "push $1\n\t"
  4106.             "push $15\n\t"
  4107.             "push Main\n\t"
  4108.             "push p_disp\n\t"
  4109.             "call XCreateSimpleWindow\n\t"
  4110.             "movl %eax, Exit\n\t"
  4111.             "add $36, %esp");
  4112.     asm(    "movl $1, %ebx\n\t"
  4113.               "shl $15, %ebx\n\t"        //ExposureMask = 1 << 15
  4114.             "movl $1, %ecx\n\t"
  4115.             "shl $2, %ecx\n\t"        //ButtonPressMask = 1 << 2
  4116.             "or %ecx, %ebx\n\t"
  4117.               "push %ebx\n\t"
  4118.             "push Exit\n\t"
  4119.             "push p_disp\n\t"
  4120.             "call XSelectInput\n\t"
  4121.             "add $12, %esp");
  4122.       gcv.foreground = pxBlack;
  4123.       gcv.background = pxWhite;
  4124.     asm(    "push $gcv\n\t"
  4125.             "movl $1, %ebx\n\t"
  4126.               "shl $14, %ebx\n\t"        //GCFont = 1 << 14
  4127.             "movl $1, %ecx\n\t"
  4128.             "shl $2, %ecx\n\t"        //GCForeground = 1 << 2
  4129.             "or %ecx, %ebx\n\t"
  4130.             "movl $1, %ecx\n\t"
  4131.             "shl $3, %ecx\n\t"        //GCBackground = 1 << 3
  4132.             "or %ecx, %ebx\n\t"
  4133.               "push %ebx\n\t"
  4134.             "push  Exit\n\t"
  4135.             "push p_disp\n\t"
  4136.             "call XCreateGC\n\t"
  4137.             "add $16, %esp\n\t"
  4138.             "movl %eax, exitGC");
  4139.     asm(  "push Exit\n\t"
  4140.             "push p_disp\n\t"
  4141.             "call XMapWindow\n\t"
  4142.             "add $8, %esp");
  4143.   /* ===== Event Loop ===== */
  4144.   while( !Done ){                 //Implemented in C to save space ;)
  4145.     XNextEvent(p_disp, &theEvent);
  4146.     if( theEvent.xany.window == Main){
  4147.       if( theEvent.type == Expose && theEvent.xexpose.count == 0){
  4148.             asm(    "push $16\n\t"
  4149.                     "push msgtext\n\t"
  4150.                     "push $40\n\t"
  4151.                     "push $1\n\t"
  4152.                     "push mainGC\n\t"
  4153.                     "push Main\n\t"
  4154.                     "push p_disp\n\t"
  4155.                     "call XDrawString\n\t"
  4156.                     "add $28, %esp");
  4157.       }
  4158.     }
  4159.     if( theEvent.xany.window == Exit){
  4160.       switch(theEvent.type){
  4161.        case Expose:
  4162.          if( theEvent.xexpose.count == 0){
  4163.            XDrawString(p_disp, Exit, exitGC, 2, 11, extext, strlen(extext) );
  4164.          }
  4165.          break;
  4166.        case ButtonPress:
  4167.          Done = 1;
  4168.       }
  4169.     }
  4170.   }
  4171. /* ===== Close Display ===== */
  4172.   asm(    "push mainGC\n\t"
  4173.           "push p_disp\n\t"
  4174.           "call XFreeGC\n\t"
  4175.           "add $8, %esp\n\t"
  4176.           "push exitGC\n\t"
  4177.           "push p_disp\n\t"
  4178.           "call XFreeGC\n\t"
  4179.           "add $8, %esp\n\t"
  4180.           "push fid\n\t"
  4181.           "push p_disp\n\t"
  4182.           "call XUnloadFont\n\t"
  4183.           "add $8, %esp\n\t"
  4184.           "push Main\n\t"
  4185.           "push p_disp\n\t"
  4186.           "call XDestroyWindow\n\t"
  4187.           "call XCloseDisplay\n\t"
  4188.           "add $8, %esp");
  4189. }
  4190. ; EOF =================================================================
  4191. As you can see, producing an XLib program in assembly language is rather 
  4192. unwieldly. The code produced is primarily data manipulations and C calls; there
  4193. is not a lot that assembly has to offer, even in the event loop. In fact, the 
  4194. only real optimization --aside from overhead added by the compiler, which in 
  4195. the above case we do not bypass-- is in the use of straight calls rather than 
  4196. the macros my original C "hello world" relied on.
  4197.  
  4198. While this in itself is somewhat of a triumph --for by coding the C application 
  4199. in assembler you learn exactly how much superfluous code there was to get rid
  4200. of-- it is not enough. In the next issue, I will cover Xt programming in 
  4201. assembler, which will use widgets/resources rather than create windows from 
  4202. scratch, therefore placing the bulk of the code in existing system libraries 
  4203. and therefore making the resultant application much smaller.
  4204.  
  4205.  
  4206. ::/ \::::::.
  4207. :/___\:::::::.
  4208. /|    \::::::::.
  4209. :|   _/\:::::::::.
  4210. :| _|\  \::::::::::.
  4211. :::\_____\:::::::::::................................ASSEMBLY.LANGUAGE.SNIPPETS
  4212.                                                                 IsASCII?
  4213.                                                                 by Troy Benoist
  4214.  
  4215. ;Summary: Routine to test whether value in AH is ASCII or not (0-127d = ASCII)
  4216. ;Compatibility: All DOS versions
  4217. ;Notes: 4 BYTES! Input: AH=value to check.
  4218.     cmp ah,80        ;8DFC80 Compare value in AH to 128 and set flags.   
  4219.     salc             ;D6     Set AL=FF if CF=1, or set AL=0 if CF=0.
  4220. ;REGISTERS DESTROYED: AL    RETURNS: AL=0 if AH is not ASCII, FF is so.
  4221.  
  4222.  
  4223.                                                                      ENUM
  4224.                                                                      by mammon_
  4225. ;Summary: A NASM macro emulating the C 'ENUM" command
  4226. ;Assembler: NASM
  4227. %macro ENUM 2-*     ;Usage: ENUM int SYMBOLS
  4228. %assign i %1        ;  where int is the number to begin enumeration at [0]
  4229. %rep %0             ;  SYMBOLS is a list of Symbols to define
  4230.   %2 EQU 0xi        ;Example: ENUM 0 TRUE FALSE
  4231.   %assign i i+1     ;  this EQUates TRUE to 0 and FALSE to 1
  4232.   %rotate 1         ;Example: ENUM 11 JACK QUEEN KING
  4233. %endrep             ;  this EQUs JACK to 11, QUEEN to 12, KING to 13
  4234. %endmacro
  4235.  
  4236.                                                                      CallTable
  4237.                                                                      by mammon_
  4238. ;Summary: Error Handler to demonstrate call-tables
  4239. ;Compatibility:
  4240. ;Notes: The EQUs define offsets from the start of ErrorHandler. Thus,
  4241. ;       ERROR_FILE_NOT_FOUND is at offset 0, ERROR_FILE_READ_ONLY is
  4242. ;       at offset 4 ( one dword from offset 0), etc.
  4243. ;       Each entry in the call table contains the address of the 
  4244. ;       code label listed there...so, in order, ErrorHandler contains 
  4245. ;       the addresses for the functions ERROR1, ERROR2, ERROR3, and 
  4246. ;       ERROR4.
  4247. ;       The code to call an error handler uses as its base
  4248. ;           call [Errorhandler]
  4249. ;       or, call the function whose address is stored at location 
  4250. ;       ErrorHandler. By adding the EQUs to this base, one gets the 
  4251. ;       offset for each function within ErrorHandler.
  4252. ERROR_FILE_NOT_FOUND    EQU     0
  4253. ERROR_FILE_READ_ONLY    EQU     4
  4254. ERROR_DISK_FULL         EQU     8
  4255. ERROR_UNKNOWN           EQU     12
  4256.  
  4257. ErrorHandler:
  4258. ;------------           Here lies the Call-Table
  4259. DWORD ERROR1
  4260. DWORD ERROR2
  4261. DWORD ERROR3
  4262. DWORD ERROR4
  4263. ;------------           Here ends the Call-Table
  4264.  
  4265. ;Handlers for various errors; offsets to these are stored in the Call-Table
  4266. ERROR1:
  4267.         ...Code to Create File...
  4268.         ret
  4269. ERROR2:
  4270.         ...Code to CHMOD File...
  4271.         ret
  4272. ERROR3:
  4273.         ...Code to Display Disk Full Message...
  4274.         jmp Exit_Program
  4275. ERROR4:
  4276.         ...Code to Display Unknown System Error-Code...
  4277.         jmp Exit_Program
  4278.  
  4279. ;Code to call Various errors
  4280.         call dword ptr [ErrorHandler + ERROR_FILE_NOT_FOUND]
  4281.         call dword ptr [ErrorHandler + ERROR_FILE_READ_ONLY]
  4282.         jmp  dword ptr [ErrorHandler + ERROR_FILE_DISK_FULL]
  4283.         jmp  dword ptr [ErrorHandler + ERROR_FILE_UNKNOWN]
  4284.  
  4285.  
  4286.  
  4287. ::/ \::::::.
  4288. :/___\:::::::.
  4289. /|    \::::::::.
  4290. :|   _/\:::::::::.
  4291. :| _|\  \::::::::::.
  4292. :::\_____\:::::::::::...........................................ISSUE.CHALLENGE
  4293.                                            PE Program Displays Its Command Line
  4294.                                            by Xbios2
  4295.  
  4296.  
  4297. The Challenge
  4298. -------------
  4299. Write the smallest possible PE program (win32) that outputs it's command line.
  4300.  
  4301. The Solution
  4302. ------------
  4303. This problem looks like the one about the 11-byte .COM program solved on the  
  4304. previous issue. Yet the method used to solve it is entirely different. This is  
  4305. because while .COM files include just raw code and data, the PE files include a 
  4306. header with information on the file. It is this header that must be 'tweaked'  
  4307. to get a small file.
  4308.  
  4309. Before going on, some things must be cleared:
  4310.  
  4311. 1. This article relies _heavily_ on "The PE File Format" by B.Luevelsmeyer  
  4312. (whom I really thank). You are advised to find the .txt and read it. Of course  
  4313. Microsoft provides it's own documentation but they would hardly ever say 'this  
  4314. seems to be ignored' for their own format.
  4315.  
  4316. 2. If you think that PE (Portable Exexutable) is the format introduced by win95  
  4317. you're wrong. Not only was PE created for winNT, but it also seems that win95  
  4318. is not 100% PE compatible. Anyway, this article has been written for winNT, and 
  4319. I don't think anything will run in windows 95.
  4320.  
  4321. 3. This article was based on a 'trial and error' method. Some solutions exist  
  4322. only because they work. So don't ask why... (Actually the trial and error 
  4323. resulted in two BSODs, thus proving that a program can crash windows NT without 
  4324. even running it's own code)
  4325.  
  4326. 4. No, I'm not paranoid. I just like pushing things to their limit :)
  4327.  
  4328. Now, on to the solution...
  4329.  
  4330. The code to print the command line looks like this:
  4331. ----------------- normal.asm -----------------------
  4332. .386
  4333. .model flat
  4334.  
  4335. extrn GetCommandLineA:proc
  4336. extrn GetStdHandle:proc
  4337. extrn WriteFile:proc
  4338.  
  4339. .data?
  4340. dummy    db ?
  4341.  
  4342. .code
  4343. start:
  4344.     call    GetCommandLineA
  4345.     xor    ecx, ecx
  4346.     push    ecx
  4347. loop1:    inc    ecx
  4348.     cmp    byte ptr [eax+ecx], 0
  4349.     jne    short loop1
  4350.     push    esp
  4351.     push    ecx
  4352.     push    eax
  4353.      push    -11
  4354.      call    GetStdHandle
  4355.     push    eax
  4356.     call    WriteFile
  4357.     ret
  4358. ends
  4359. end start
  4360. ----------------------------------------------------
  4361. some comments on the code:
  4362. - the .data? section is present because I can't make TASM work without any data
  4363. - there is no ExitProcess. In it's place there is a simple 'ret'. This is 
  4364. because the entry point is actually called by kernel32 with the following piece 
  4365. of code:
  4366.  
  4367.     call    [ebp+8]        ; [ebp+8] holds the entry point address
  4368.     push    eax
  4369.     jmp    label:
  4370.     ...
  4371. label:    call    ExitThread
  4372.  
  4373. This program compiles under TASM to 4 KB long. Those 4096 bytes are divided 
  4374. like this:
  4375.  
  4376. Dos Stub             256
  4377. PE Header            248
  4378. 4 section headers    160
  4379. padding              872
  4380. ------------------------
  4381. code                  50
  4382. padding              462
  4383. imports              132
  4384. padding              380
  4385. reloc                 16
  4386. padding             1520
  4387.  
  4388. This means that we have:
  4389. 16%  header
  4390.  5%  code / data
  4391. 79%  padding
  4392.  
  4393. It seems that TASM can't create anything smaller. So, the code will have to be 
  4394. written by hand in a hex editor. Actually you don't have to worry, as you'll 
  4395. only have to write 192 bytes for the final program (believe it or not!).
  4396.  
  4397. In order to shrink the file, the following steps must be taken: Remove Padding,
  4398. Use a Single Section, Remove the DOS Stub, Tweak the PE Header, Squeeze the
  4399. Code, Squeeze the Imports, and 'ReAssemble' the Program.
  4400.  
  4401. 1. Remove padding
  4402. -----------------
  4403. By changing the 'FileAlignment' field in the PE header, all the padding can be 
  4404. discarded. (Actually it seems that win95 won't allow this)
  4405.  
  4406. 2. Use one section
  4407. ------------------
  4408. TASM creates the following sections:
  4409.  
  4410. .code    : code
  4411. .data    : initialized and uninitialized data
  4412. .idata    : imports
  4413. .reloc    : relocation info
  4414.  
  4415. -The .reloc section is not needed, as only DLLs get relocated
  4416. -The .data sectionis only present because I can't have TASM create a normal 
  4417. executable without a data section.
  4418. -The .idata section can then be merged with the .code section. Remember that the 
  4419. name of each section does not depend on what the section contains, since the OS 
  4420. finds things like imports, relocations or resources from the directory in the 
  4421. PE header.
  4422.  
  4423. 3. No DOS stub
  4424. --------------
  4425. All compilers that compile PE executables create a DOS stub that displays a 
  4426. message like 'This program must be run under Win32'. Yet this is NOT required 
  4427. by the PE format. What PE needs (as seen in [ntdll.dll]RtlImageNtHeader or 
  4428. [imagehlp.dll]ImageNtHeader) is:
  4429.  
  4430. PIECE I: DOS HEADER
  4431. ---------------------------------------------
  4432. 0000| 4D5A **** **** **** **** **** **** ****
  4433. 0010| **** **** **** **** **** **** **** ****
  4434. 0020| **** **** **** **** **** **** **** ****
  4435. 0030| **** **** **** **** **** **** ???? ????
  4436.  
  4437. where ???? is the offset of the PE header from the beginning of the file
  4438.  
  4439. 4. Tweaked PE header
  4440. --------------------
  4441. The PE header consists of the following structures:
  4442.  
  4443. IMAGE_NT_SIGNATURE: 00004550h
  4444. IMAGE_FILE_HEADER:
  4445.     WORD    Machine                 ; >> 014Ch for Intel 386
  4446.     WORD    NumberOfSections        ; 1 for this example
  4447.     DWORD   TimeDateStamp           ; *
  4448.     DWORD   PointerToSymbolTable    ; *
  4449.     DWORD   NumberOfSymbols         ; *
  4450.     WORD    SizeOfOptionalHeader    ; >> 70h (Opt. header + directories)
  4451.     WORD    Characteristics         ; >> 0102h for 32bit executable
  4452. IMAGE_OPTIONAL_HEADER:
  4453.     WORD    Magic                   ; 0B01h
  4454.     BYTE    MajorLinkerVersion        ; *
  4455.     BYTE    MinorLinkerVersion        ; *
  4456.     DWORD   SizeOfCode              ; *
  4457.     DWORD   SizeOfInitializedData    ; *
  4458.     DWORD   SizeOfUninitializedData    ; *
  4459.     DWORD   AddressOfEntryPoint        ; >> ???? RVA of entry point
  4460.     DWORD   BaseOfCode              ; *
  4461.     DWORD   BaseOfData              ; *
  4462.     DWORD   ImageBase               ; >> 00100000h for this example
  4463.     DWORD   SectionAlignment        ; 2
  4464.     DWORD   FileAlignment           ; 2
  4465.     WORD    MajorOperatingSystemVersion     ; *
  4466.     WORD    MinorOperatingSystemVersion     ; *
  4467.     WORD    MajorImageVersion        ; *
  4468.     WORD    MinorImageVersion        ; *
  4469.     WORD    MajorSubsystemVersion    ; >> 0004
  4470.     WORD    MinorSubsystemVersion    ; >> 0000
  4471.     DWORD   Win32VersionValue        ; *
  4472.     DWORD   SizeOfImage             ; >> ????
  4473.     DWORD   SizeOfHeaders           ; *
  4474.     DWORD   CheckSum                ; *
  4475.     WORD    Subsystem               ; 0003 for win32 console application
  4476.     WORD    DllCharacteristics        ; *
  4477.     DWORD   SizeOfStackReserve        ; 00100000h
  4478.     DWORD   SizeOfStackCommit        ; 00001000h
  4479.     DWORD   SizeOfHeapReserve        ; 00100000h
  4480.     DWORD   SizeOfHeapCommit        ; 00001000h
  4481.     DWORD   LoaderFlags             ; *
  4482.     DWORD   NumberOfRvaAndSizes        ; 2 data directories (Exports & Imports)
  4483. ...a number (actually 2) of the following:
  4484. IMAGE_DATA_DIRECTORY:
  4485.     DWORD   VirtualAddress          ; 0 for exports, ???? for imports
  4486.     DWORD   Size                    ; 0 for exports, ???? for imports
  4487. ...a number (actually 1) of the following:
  4488. IMAGE_SECTION_HEADER:
  4489.     BYTE    Name[8]                 ; * (Anything we like)
  4490.     DWORD   VirtualSize             ; ?! (h.o. word must be zero??)
  4491.     DWORD   VirtualAddress          ; >> ????
  4492.     DWORD   SizeOfRawData           ; >> ????
  4493.     DWORD   PointerToRawData        ; >> ????
  4494.     DWORD   PointerToRelocations    ; *
  4495.     DWORD   PointerToLinenumbers    ; *
  4496.     WORD    NumberOfRelocations        ; *
  4497.     WORD    NumberOfLinenumbers        ; *
  4498.     DWORD   Characteristics         ; *
  4499.  
  4500. So the raw hex data for the PE header are:
  4501. PIECE II: PE HEADER
  4502. ---------------------------------------------
  4503.     | 5045 0000 4C01 0100 **** **** **** ****
  4504.     | **** **** 7000 0201 0B01 **** **** ****
  4505.     | **** **** **** **** ???? ???? **** ****
  4506.     | **** **** 0000 1000 0200 0000 0200 0000
  4507.     | **** **** **** **** 0400 0000 **** ****
  4508.     | ???? ???? **** **** **** **** 0300 ****
  4509.     | 0000 1000 0010 0000 0000 1000 0010 0000
  4510.     | **** **** 0200 0000 0000 0000 0000 0000
  4511.     | ???? ???? ???? ???? **** **** **** ****
  4512.     | **** **** ???? ???? ???? ???? ???? ????
  4513.     | **** **** **** **** **** **** **** ****
  4514.  
  4515. NOTES:
  4516. - ???? means that the value is needed but has to be filled in later as it 
  4517. depends on the code
  4518. - **** means that the value is either completely ignored or it can be set to 
  4519. any value without raising an error
  4520. - the main difference between this and a 'normal' PE header is that the size of 
  4521. the optional header is 70h (112 bytes) instead of the standard 0E0h (224 bytes). 
  4522. This is because there are only 2 directories instead of 16. This seems to be 
  4523. the minimum number of directories possible, as there seems to be no way of 
  4524. running an .exe that has no imports.
  4525.  
  4526. 5. Squeezed code
  4527. ----------------
  4528. Even though the code we have is already tight, it has one major drawback: It 
  4529. invokes three API functions. To realize what this means just think that the 
  4530. names of the functions are included in the imports section as normal ASCII 
  4531. which means that only the names would take 36 bytes...
  4532.  
  4533. The solution here (since those functions are needed) is to call the functions 
  4534. directly. This is possible because kernel32.dll is never relocated so the 
  4535. function entry points are always the same (for a given version of windows).
  4536.  
  4537. For NT4 those values are:
  4538. GetStdHandle: 77F01CBB
  4539. WriteFile   : 77F0D354
  4540.  
  4541. GetCommandLine is a special case since it has the format:
  4542.   GetCommandLineA proc near
  4543.         mov     eax, [77F4657Ch]
  4544.         retn
  4545.   GetCommandLineA endp
  4546.  
  4547. so the final code will look like:
  4548. ----------------- code.hex -----------------------
  4549. A17C65F477    mov    eax, offset CommandLine
  4550. BEBB1CF077    mov    esi, offset GetStdHandle
  4551. 33C9        xor    ecx, ecx
  4552. 51          push    ecx
  4553. 41          inc ecx
  4554. 803C0800    cmp    [eax+ecx], 0
  4555. 75F9        jnz    -07
  4556. 54          push    esp
  4557. 51          push    ecx
  4558. 50          push    eax
  4559. 6AF5        push   -11 ; StdOut
  4560. FFD6        call   esi ; GetStdHandle
  4561. 50          push    eax
  4562. B854D3F077    mov    eax, offset WriteFile
  4563. FFD0        call    eax
  4564. C3          ret
  4565. --------------------------------------------------
  4566.  
  4567. 6. Squeezed imports
  4568. -------------------
  4569. [Comment: read a text on PE format to better understand what's going on]
  4570. As mentioned earlier, the PE file must have an imports directory in order to 
  4571. load properly. Yet, since we call API functions directly, we only have to 
  4572. specify one dummy import. A good choice (since it really has a short name) is 
  4573. 'Arc' from 'gdi32.dll'. To specify this imported function we should need:
  4574.  
  4575. IMAGE_IMPORT_DESCRIPTOR for gdi32.dll:
  4576.   OriginalFirstThunk        ; *
  4577.   TimeDateStamp             ; *
  4578.   ForwarderChain            ; *
  4579.   Name                      ; >> ???? RVA of ASCII string 'gdi32.dll',0
  4580.   FirstThunk                ; >> ???? RVA described later... 
  4581. IMAGE_IMPORT_DESCRIPTOR full of zeroes to specify end of imports
  4582.   OriginalFirstThunk        ; *
  4583.   TimeDateStamp             ; *
  4584.   ForwarderChain            ; *
  4585.   Name                      ; 0 This is checked to see if it is the end...
  4586.   FirstThunk                ; *
  4587.  
  4588. 'FirstThunk' is the RVA of a 0-terminated list of RVAs, one for each function 
  4589. in the specified DLL. For this example we only need one RVA followed by a null 
  4590. dword. This RVA will point to a structure IMAGE_IMPORT_BY_NAME:
  4591.     WORD    Hint            ; *
  4592.     BYTE    Name[...]       ; 'Arc',0
  4593.  
  4594. By putting all this together we would have:
  4595.  
  4596. PIECE III: IMPORTS
  4597. ---------------------------------------------
  4598.     | **** **** **** **** **** **** -dword 1-
  4599.     | -dword 2- -dword 3- 0000 0000 **** **** 
  4600.     | 0000 0000 **** **** 
  4601.  
  4602. dwords 1 and 2 are the two RVAs for the IMAGE_IMPORT_DESCRIPTOR. dword 3 is the 
  4603. RVA to the IMAGE_IMPORT_BY_NAME. So, dword 2 is the RVA of dword 3. We also 
  4604. need space for the two strings 'gdi32.dll',0 and 'Arc',0.
  4605.  
  4606. There is a way to use even less bytes for the imports. Just remember that the 
  4607. imports are examined after the file has been mapped into memory. So, since 
  4608. memory is allocated in blocks, after the end of the file there will be a space 
  4609. full of zeroes. So by placing the three dwords in the last 12 bytes of the file, 
  4610. there is no need for the two zeroes.
  4611.  
  4612. 7. 'Assemble' the program
  4613. -------------------------
  4614. The values marked as ???? will be:
  4615. Offset of PE header     :   00000010
  4616. AddressOfEntryPoint     :   00000002
  4617. SizeOfImage             :   000000C0
  4618. Imports RVA             :   000000A8
  4619. Imports Size            :   00000028
  4620. Section VirtualAddress  :   00000000
  4621. Section SizeOfRawData   :   000000C0
  4622. Section PointerToRawData:   00000000
  4623. Dll Name RVA            :   00000098
  4624. Dll FirstThunk RVA      :   000000BC
  4625. Dll Function Hint/Name  :   000000AE
  4626.  
  4627. Notice that the Section data and the Header (DOS and PE) are the same thing. 
  4628. The section RVA is 0, so file offset and RVAs are the same. The code will be 
  4629. broken in three pieces, connected by two jumps. The final result will be:
  4630.  
  4631. THE PROGRAM
  4632. ---------------------------------------------
  4633. 0000| 4D5A A17C 65F4 77BE BB1C F077 33C9 EB08
  4634. 0010| 5045 0000 4C01 0100 5141 803C 0800 75F9
  4635. 0020| 5451 EB06 7000 0201 0B01 506A F5FF D650
  4636. 0030| B854 D3F0 77FF D0C3 0200 0000 1000 0000
  4637. 0040|           0000 1000 0200 0000 0200 0000
  4638. 0050|                     0400 0000
  4639. 0060| C000 0000                     0300
  4640. 0070| 0000 1000 0010 0000 0000 1000 0010 0000
  4641. 0080|           0200 0000 0000 0000 0000 0000
  4642. 0090| A800 0000 2800 0000 6764 6933 322E 646C
  4643. 00A0| 6C00 0000 0000 0000 C000 0000 0000 0000
  4644. 00B0| 4172 6300 9800 0000 BC00 0000 AE00 0000
  4645.  
  4646. Blank bytes are meaningless, and can be set to any value.
  4647.  
  4648.  
  4649. Wrapping Up
  4650. -----------
  4651. Well, if you managed to read up to here, and understood what happened, I guess 
  4652. you need no more explanations. I just gave an idea (actually MANY ideas). Maybe 
  4653. on another article I will start exploring the possibilities this 'experiment' 
  4654. showed me... 
  4655.  
  4656. Next Issue Challenge
  4657. --------------------
  4658. Write a routine for converting ASCII hex to binary in 6 bytes.
  4659.  
  4660.  
  4661. ::/ \::::::.
  4662. :/___\:::::::.
  4663. /|    \::::::::.
  4664. :|   _/\:::::::::.
  4665. :| _|\  \::::::::::.
  4666. :::\_____\:::::::::::.......................................................FIN
  4667.  
  4668.